1. 程式人生 > >URAL 1989 Subpalindromes (多項式hash)【線段樹】

URAL 1989 Subpalindromes (多項式hash)【線段樹】

區間 sum 題目 fin cst 方式 取模 hup space

<題目鏈接>

<轉載於 >>> >

題目大意:
給你一段字符串,進行兩種操作:
1.詢問[l,r]這個區間中的字符串是否是回文串;

2.更改該字符串中對應下標的字符。

解題分析:

快速判斷字符串是不是回文串,可以用到多項式Hash。假設一個串s,那麽字串s[i, j]的Hash值就是H[i, j]=s[i]+s[i+1]*x+s[i+2]*(x^2)+...+s[j]*(x^(j-i))。由於只有小寫字母,因此x取27。但是H[i, j]這會很大,我們取模就可了,可以把變量類型設為unsigned long long, 那麽自動溢出就相當於模2^64了。對於不同串但是Hash相同的情況,這種情況的概率是非常小的,通常可以忽略,當然我們也可以對x取多次值,求出不同情況下的Hash值。然後我們就可以用線段樹或者樹狀數組來維護這個和了,復雜度O(nlogn)。

 1 #include <cstdio>
 2 #include <cstring>
 3 #include <algorithm>
 4 using namespace std;
 5 
 6 #define Lson rt<<1,l,mid
 7 #define Rson rt<<1|1,mid+1,r
 8 #define N 100005
 9 #define ull unsigned long long 
10 ull f[N];
11 char s[N];
12 int n;
13 struct Tree{
14 ull lsum,rsum; //左->右和右->左的hash值 15 }tr[N<<2]; 16 void Pushup(int rt){ //將該區間內從左到右和從右到左的多項式hash的每一位相加 17 tr[rt].lsum=tr[rt<<1].lsum+tr[rt<<1|1].lsum; 18 tr[rt].rsum=tr[rt<<1].rsum+tr[rt<<1|1].rsum; 19 } 20 void build(int rt,int l,int r){ 21 if
(l==r){ 22 tr[rt].lsum=f[l-1]*(s[l-1]-a); //得到hash多項式相應位置的hash值 23 tr[rt].rsum=f[n-l]*(s[l-1]-a); 24 return; 25 } 26 int mid=(l+r)>>1; 27 build(Lson); 28 build(Rson); 29 Pushup(rt); 30 } 31 void update(int rt,int l,int r,int pos,int num){ 32 if(l==r){ 33 tr[rt].lsum=f[l-1]*num; 34 tr[rt].rsum=f[n-l]*num; 35 return; 36 } 37 int mid=(l+r)>>1; 38 if(pos<=mid)update(Lson,pos,num); 39 if(pos>mid)update(Rson,pos,num); 40 Pushup(rt); 41 } 42 ull lsum,rsum; 43 void query(int rt,int l,int r,int L,int R){ 44 if(L<=l&&r<=R){ 45 lsum+=tr[rt].lsum; 46 rsum+=tr[rt].rsum; 47 return; 48 } 49 int mid=(l+r)>>1; 50 if(L<=mid)query(Lson,L,R); 51 if(R>mid)query(Rson,L,R); 52 } 53 int main(){ 54 f[0]=1; 55 for(int i=1;i<N;i++) 56 f[i]=f[i-1]*27; //預處理27的1~N次方 57 while(scanf("%s",s)!=EOF){ 58 n=strlen(s); 59 build(1,1,n); 60 int q;scanf("%d",&q); 61 while(q--){ 62 scanf("%s",s); 63 if(s[0]==p){ 64 int x,y;scanf("%d%d",&x,&y); 65 lsum=rsum=0; 66 query(1,1,n,x,y); 67 int k1=x-1; 68 int k2=n-y; 69 if(k1>k2)rsum*=f[k1-k2]; //按照上面的計算方式,從左向右和從右向左的相同hash多項式的計算中,短區域中的每一項會比長區域少乘f[k1-k2](或f[k2-k1])次方,所以這裏要講相差的f[]乘上,再進行比較 70 else lsum*=f[k2-k1]; 71 if(lsum==rsum)printf("Yes\n"); 72 else printf("No\n"); 73 } 74 else{ 75 int x; 76 scanf("%d%s",&x,s); 77 update(1,1,n,x,s[0]-a); 78 } 79 } 80 } 81 }

2018-10-31

URAL 1989 Subpalindromes (多項式hash)【線段樹】