KMP专题
1、【HDU 3336】Count the string(KMP+dp)
题意:求给定字符串含前缀的数量,如输入字符串abab,前缀是a、ab、aba、abab,在原字符串中出现的次数分别是2、2、1、1,所以答案是2+2+1+1=6.
解题思路:s[]=abcdabcdabcdea ==> f[] = 00001234567801,f[i]=k的含义是s[i-k]=s[i],dp[i]=dp[f[i]]+1,dp[i]表示以s[i]结尾的前缀的数量
1 #include <iostream> 2 #include <algorithm> 3 #include <cstdio> 4 #include <cstring> 5 #include <string> 6 using namespace std; 7 #define ll __int64 8 const int N=2*1e5+10; 9 const ll mod=10007; 10 char p[N]; 11 ll f[N], ans[N]; 12 int lenp; 13 void getf() 14 { 15 memset(f, 0, sizeof(f)); 16 int i,j; 17 j=f[1]=0; 18 i=1; 19 while(i<lenp) 20 { 21 while(0!=j && p[i+1]!=p[j+1]) j=f[j]; 22 if(p[i+1] == p[j+1]) j++; 23 f[++i] = j; 24 } 25 } 26 int main(){ 27 int t; 28 scanf("%d", &t); 29 while(t--){ 30 memset(ans, 0, sizeof(ans)); 31 memset(p, '\0', sizeof(p)); 32 scanf("%d%s", &lenp, p+1); 33 getf(); 34 ll sum=0; 35 for(int i=1; i<=lenp; i++) ans[i] = (ans[f[i]]+1)%mod; 36 for(int i=1; i<=lenp; i++) sum = (sum+ans[i])%mod; 37 printf("%I64d\n", sum); 38 // for(int i=1; i<=lenp; i++) cout<<f[i]; 39 //\ cout<<endl; 40 } 41 return 0; 42 }
题意:如果字符串a=”abc”,字符串b=”def”,那么a*b=”abcdef”,输入字符串s,那么s=x^n中n的最大值。
解题思路:求n的最大值也就是求s的最小循环节【s[]=abcdabcdabcdea ==> f[] = 00001234567801,最小循环节只能是len-f[len]】然后判断len是否能整除最小循环节,如果不能整除,那么n最大值只能是1
1 #include <iostream> 2 #include <algorithm> 3 #include <cstdio> 4 #include <cstring> 5 #include <string> 6 using namespace std; 7 const int N=1000005; 8 char s[N]; 9 int f[N], lens; 10 void getf(){ 11 memset(f, 0, sizeof(f)); 12 int i, j; 13 j=f[0]=-1; 14 i=0; 15 while(i<lens){ 16 while(j!=-1 && s[i+1]!=s[j+1]) j = f[j]; 17 f[++i] = ++j; 18 } 19 } 20 int main(){ 21 while(~scanf("%s", s)){ 22 lens = strlen(s); 23 if(lens==1 && s[0]=='.') break; 24 getf(); 25 f[lens-1]++; 26 int ans = lens-f[lens-1]; 27 if(lens%ans==0) ans = lens/ans; 28 else ans=1; 29 printf("%d\n", ans); 30 31 } 32 return 0; 33 }
3、【HDU 1867】A + B for you again
题意:输入两个字符串(len<=1e5)A、B,s1=A+B【去掉了A的后缀跟B的前缀相等部分】,s2=B+A【去掉了B的后缀跟A的前缀相等部分】,输出s1跟s2其中一个,先考虑长度小的,长度差相等的时候考虑字典序小的。
解题思路:关键是求出A的后缀与B的前缀以及B的后缀跟A的前缀重叠部分的长度。A的后缀跟B的前缀重叠部分的求法:求出B的fp[],拿B去匹配A,如果能一直匹配到最后一位,那么重叠部分就是B已经匹配了的长度。
1 #include <iostream> 2 #include <algorithm> 3 #include <cstdio> 4 #include <cstring> 5 #include <string> 6 using namespace std; 7 const int N=1e5+10; 8 char s[N], p[N]; 9 int fs[N], fp[N], lens, lenp; 10 void kmp_getfs(){ 11 memset(fs, 0, sizeof(fs)); 12 int i, j; 13 i=2, j=fs[1]=0; 14 while(i<=lens){ 15 while(j!=0 && s[i]!=s[j+1]) j = fs[j]; 16 if(s[i] == s[j+1]) j++; 17 fs[i++]=j; 18 } 19 } 20 void kmp_getfp(){ 21 memset(fp, 0, sizeof(fp)); 22 int i, j; 23 i=2, j=fp[1]=0; 24 while(i<=lenp){ 25 while(j!=0 && p[i]!=p[j+1]) j=fp[j]; 26 if(p[i] == p[j+1]) j++; 27 fp[i++] = j; 28 } 29 } 30 int main(){ 31 while(~scanf("%s%s", s+1, p+1)){ 32 lens = strlen(s+1); 33 lenp = strlen(p+1); 34 kmp_getfs(); 35 kmp_getfp(); 36 int is=1, jp=0, flags=0; 37 for(is=1; is<=lens; is++){ 38 while(jp!=0 && s[is]!=p[jp+1]) jp=fp[jp]; 39 if(s[is]==p[jp+1]){ 40 jp++; 41 if(is==lens) flags=1; 42 } 43 } 44 int ip=1, js=0, flagp=0; 45 for(ip=1; ip<=lenp; ip++){ 46 while(js!=0 && p[ip]!=s[js+1]) js = fs[js]; 47 if(p[ip] == s[js+1]){ 48 js++; 49 if(ip==lenp) flagp=1; 50 } 51 } 52 int sums=lens+lenp-jp; 53 int sump=lens+lenp-js; 54 if(sums==sump){ 55 string sp, ps; 56 sp.clear(), ps.clear(); 57 for(int i=1; i<=lens; i++) sp+=s[i]; 58 for(int i=jp+1; i<=lenp; i++) sp+=p[i]; 59 for(int i=1; i<=lenp; i++) ps+=p[i]; 60 for(int i=js+1; i<=lens; i++) ps+=s[i]; 61 if(sp>ps) cout<<ps<<endl; 62 else cout<<sp<<endl; 63 } 64 else if(sums>sump){ 65 printf("%s", p+1); 66 for(int i=js+1; i<=lens; i++) cout<<s[i]; 67 cout<<endl; 68 } 69 else if(sums<sump){ 70 printf("%s", s+1); 71 for(int i=jp+1; i<=lenp; i++) cout<<p[i]; 72 cout<<endl; 73 } 74 } 75 return 0; 76 }