【UVA10829】 L-Gap Substrings (后缀数组)
Description
If a string is in the form UVU, where U is not empty, and V has exactly L characters, we say UVU
is an L-Gap string. For example, abcbabc is a 1-Gap string. xyxyxyxyxy is both a 2-Gap string
and also a 6-Gap string, but not a 10-Gap string (because U is non-empty).
Given a string s, and a positive integer g, you are to find the number of g-Gap substrings in s. s
contains lower-case letters only, and has at most 50,000 characters.Input
The first line contains a single integer t (1 ≤ t ≤ 10), the number of test cases. Each of the t followings
contains an integer g (1 ≤ g ≤ 10) followed by a string s.Output
For each test case, print the case number and the number of g-Gap substrings. Look at the output for
sample input for details.Sample Input
2
1 bbaabaaaaa
5 abxxxxxab
Sample Output
Case 1: 7
Case 2: 1
【题意】
UVU形式的串的个数,V的长度规定,U要一样,位置不一样即为不同字串
【分析】
表示做了poj3693还是不会做这题。
为什么会想到枚举L然后分块呢????
为什么呢????
这种方法于我而言还是有点难理解的啊。
主要是分块!!
任意一个满足条件的UVU,假设U的长度是len,那么左端的U必然包含按照len切分的T串的某个字串,及0,len,2len,3len...。(这点要仔细想清楚)
那么枚举每个端点i*len,然后利用RMQ求后缀i*len和后缀i*len+L+len的LCP,然后字符串T反向,再求一遍反向的LCP2。(其中LCP要小于等于len,否则会重复,仔细想清楚)
最后累加求和sum+=(LCP+LCP2-1)-len+1。(这点想清楚为什么是-len)
blog:http://blog.csdn.net/u011526463/article/details/14000693
还有,其实,貌似不用后缀数组直接两个while前后就可以了。时间貌似还是nlogn的。 【(2017-03-24 14:43:33)许多年后发现以前在搞笑。。】
代码如下:
1 #include<cstdio> 2 #include<cstdlib> 3 #include<cstring> 4 #include<iostream> 5 #include<algorithm> 6 #include<queue> 7 using namespace std; 8 #define Maxl 100010 9 10 int l,len; 11 char s[Maxl]; 12 int c[Maxl],cl; 13 14 int mymin(int x,int y) {return x<y?x:y;} 15 16 void init() 17 { 18 scanf("%d%s",&l,s); 19 len=strlen(s); 20 cl=0; 21 for(int i=0;i<len;i++) c[++cl]=s[i]-'a'+1; 22 } 23 24 int sa[Maxl],rk[Maxl],y[Maxl],wr[Maxl],Rs[Maxl]; 25 void get_sa(int m) 26 { 27 memcpy(rk,c,sizeof(rk)); 28 for(int i=0;i<=m;i++) Rs[i]=0; 29 for(int i=1;i<=cl;i++) Rs[rk[i]]++; 30 for(int i=1;i<=m;i++) Rs[i]+=Rs[i-1]; 31 for(int i=cl;i>=1;i--) sa[Rs[rk[i]]--]=i; 32 33 int ln=1,p=0; 34 while(p<cl) 35 { 36 int k=0; 37 for(int i=cl-ln+1;i<=cl;i++) y[++k]=i; 38 for(int i=1;i<=cl;i++) if(sa[i]>ln) y[++k]=sa[i]-ln; 39 for(int i=1;i<=cl;i++) wr[i]=rk[y[i]]; 40 41 for(int i=0;i<=m;i++) Rs[i]=0; 42 for(int i=1;i<=cl;i++) Rs[wr[i]]++; 43 for(int i=1;i<=m;i++) Rs[i]+=Rs[i-1]; 44 for(int i=cl;i>=1;i--) sa[Rs[wr[i]]--]=y[i]; 45 46 for(int i=1;i<=cl;i++) wr[i]=rk[i]; 47 for(int i=cl+1;i<=cl+ln;i++) wr[i]=0; 48 p=1,rk[sa[1]]=1; 49 for(int i=2;i<=cl;i++) 50 { 51 if(wr[sa[i]]!=wr[sa[i-1]]||wr[sa[i]+ln]!=wr[sa[i-1]+ln]) p++; 52 rk[sa[i]]=p; 53 } 54 ln*=2,m=p; 55 } 56 sa[0]=rk[0]=0; 57 } 58 59 int height[Maxl]; 60 void get_he() 61 { 62 int k=0; 63 for(int i=1;i<=cl;i++) if(rk[i]!=1) 64 { 65 int j=sa[rk[i]-1]; 66 if(k) k--; 67 while(c[i+k]==c[j+k]&&i+k<=cl&&j+k<=cl) k++; 68 height[rk[i]]=k; 69 } 70 } 71 72 int d[Maxl][20]; 73 void rmq_init() 74 { 75 for(int i=1;i<=cl;i++) d[i][0]=height[i]; 76 for(int j=1;(1<<j)<=cl;j++) 77 for(int i=1;i+(1<<j)-1<=cl;i++) 78 d[i][j]=mymin(d[i][j-1],d[i+(1<<j-1)][j-1]); 79 } 80 81 int rmq(int x,int y) 82 { 83 int t;x=rk[x];y=rk[y]; 84 if(x>y) t=x,x=y,y=t; 85 x++; 86 int k=0; 87 while((1<<k+1)<=y-x+1) k++; 88 return mymin(d[x][k],d[y-(1<<k)+1][k]); 89 } 90 91 92 void ffind() 93 { 94 int ans=0; 95 for(int i=1;i<=cl;i++) 96 { 97 for(int j=0;j<=cl/i;j++) 98 { 99 int now=j*i+1,x,y=0; 100 if(c[now]!=c[now+l+i]||now+l+i>cl) continue; 101 x=mymin(i,rmq(now,now+l+i));//向后匹配 102 while(c[now-y-1]==c[now+l+i-y-1]&&y+1<i) y++;//向前匹配 103 if(x+y-i+1>0) ans+=x+y-i+1; 104 } 105 } 106 printf("%d\n",ans); 107 } 108 109 int main() 110 { 111 int T,kase=0; 112 scanf("%d",&T); 113 while(T--) 114 { 115 init(); 116 get_sa(30); 117 get_he(); 118 rmq_init(); 119 printf("Case %d: ",++kase); 120 ffind(); 121 } 122 return 0; 123 }
2016-07-19 15:44:08
这道题我看了100遍!!
现在又明白了一点了ORZ。。。分块屌ORZ。。。
好吧,是按照u的长度分块!!
为什么呢,目的是:答案串的u至少包含一个分割点(上面的蓝色突起)
我们对于其包含的第一个分割点时计算他!!!(就是上面红色圈起的部分)
如果匹配长度越过第二个分割点,那么是会重复计算的,所以在这一题,向前匹配和向后匹配都可以直接while,越过分割点的时候就结束。
ORZ。。。
2016-11-13 14:52:25