hdu 3336 (Count the string) next数组+dp
这一题很早很早以前就遇到了,一直一来迟迟没有动手。
直到昨天为止,做了足够多的”前戏“,才敢下手。
题目大意:给定一个字符串,比如 abab,找出i(1=<i<=n)个单位长度的前缀在字符串中出现的次数和。
前缀 出现次数
a 2
ab 2
aba 1
abab 1
所以,答案就是6;
用dp[i]记录字符串中前i个字符中所包含的前缀个数。
i dp[i] 包含的前缀 next[i]
1 1 a 0
2 1 ab 0
3 2 a,aba 1
4 2 ab,abab 2
通过上面的列表可以发现,前i个字符至少含有一个前缀,那就是它本身;
当i=3时,为何有两个前缀?有nex数组的意义可以知道:因为next[3]=1,即从1到3是一个循环周期,而在这个循环内是不会再含有更多的前缀了!
所以当求前i个字符所包含的前缀个数时,只需考虑他的上一个循环周期的dp[next[i]]再加他本身就ok了;
状态转移方程:
dp[i]=dp[next[i]]+1;
1 #include<iostream> 2 #include<cstring> 3 #include<cstdio> 4 using namespace std; 5 #define N 200005 6 int next[N]; 7 char str[N]; 8 int dp[N]; 9 int t,n; 10 void get_next() 11 { 12 13 int i=0,j=-1; 14 next[0]=-1; 15 while(i<n) 16 { 17 18 if(j==-1||str[i]==str[j]) 19 { 20 21 i++; 22 j++; 23 next[i]=j; 24 } 25 else 26 j=next[j]; 27 28 } 29 // for(i=1;i<=n;i++) 30 // printf("%d ",next[i]); 31 // printf("\n"); 32 } 33 int main() 34 { 35 // freopen("input.txt","r",stdin); 36 // freopen("output.txt","w",stdout); 37 38 scanf("%d",&t); 39 while(t--) 40 { 41 42 scanf("%d",&n); 43 getchar(); 44 scanf("%s",str); 45 memset(next,-1,sizeof(next)); 46 get_next(); 47 int i; 48 memset(dp,0,sizeof(dp)); 49 int sum=0; 50 for(i=1;i<=n;i++) 51 { 52 53 dp[i]=(dp[next[i]]+1)%10007; 54 sum=(sum+dp[i])%10007; 55 } 56 57 // for(i=1;i<=n;i++) 58 // printf("%d ",dp[i]); 59 // printf("\n"); 60 61 printf("%d\n",sum); 62 } 63 return 0; 64 }