BZOJ 1009 [HNOI2008]GT考试 (KMP+矩阵乘法)
---恢复内容开始---
题目大意:给定一个由数字构成的字符串A(len<=20),让你选择一个长度为n(n是给定的)字符串X,一个合法的字符串X被定义为,字符串X中不存在任何一段子串与A完全相同,求互不相同的合法的字符串L的数量
第一眼看就没啥思路....瞅了一眼题解,是KMP优化DP,然后再用矩阵优化DP
思路还是不难的,首先用KMP求出原字符串的next数组,再用next转移
定义f[i][j]是当前X串匹配到了第i位,已经匹配到了字符串A的第j位
每次在X串的第j+1位填上一个数c,那么X串现在最长能匹配上A串的位置
就是从第j+1位一直往前跳next,直到碰到一个位置a[k]==a[j]或k==0也匹配不到
1 int k=i+1; 2 for(k=i+1;k>0&&a[k]!=c;k=nxt[k]) 3 ; 4 pw.mp[k][i]++;
这是一个连续的过程,上面是构建矩阵的核心代码(原来的代码太丑了我改了一下)
至于为什么要这么跳呢,这是一个类似于"贪心"的过程,但并不是我们主动去贪心
因为我们要保证每次转移的位置都是正确的
然后发现N<=1e9有点大,矩阵乘法优化一下即可
1 #include <map> 2 #include <cmath> 3 #include <cstdio> 4 #include <cstring> 5 #include <algorithm> 6 #define ll long long 7 #define N 23 8 #define ui unsigned int 9 #define inf 0x3f3f3f3f 10 using namespace std; 11 //re 12 int n,len; 13 ui mod; 14 char str[N]; 15 int a[N],nxt[N]; 16 struct mtx{ 17 ui mp[N][N]; 18 friend mtx operator *(const mtx &s1,const mtx &s2) 19 { 20 mtx ret;memset(&ret,0,sizeof(ret)); 21 for(int i=0;i<len;i++) 22 for(int j=0;j<len;j++) 23 for(int k=0;k<len;k++) 24 (ret.mp[i][j]+=(s1.mp[i][k]*s2.mp[k][j])%mod)%=mod; 25 return ret; 26 } 27 mtx qpow(mtx &ans,mtx &x,int y) 28 { 29 while(y){ 30 if(y&1) ans=x*ans; 31 x=x*x;y>>=1; 32 } 33 } 34 }M; 35 void get_kmp() 36 { 37 int i=1,j=0; 38 nxt[1]=0; 39 while(i<=len) 40 if(j==0||a[i]==a[j]) 41 i++,j++,nxt[i]=j; 42 else j=nxt[j]; 43 } 44 45 int main() 46 { 47 scanf("%d%d%u",&n,&len,&mod); 48 scanf("%s",str+1); 49 for(int i=1;i<=len;i++) a[i]=str[i]-'0'; 50 get_kmp(); 51 mtx pw;memset(&pw,0,sizeof(pw)); 52 for(int i=0;i<len;i++) 53 for(int c=0;c<=9;c++) 54 { 55 if(i==len-1&&a[len]==c) continue; 56 int k=i+1; 57 for(k=i+1;k>0&&a[k]!=c;k=nxt[k]); 58 pw.mp[k][i]++; 59 } 60 mtx ret;memset(&ret,0,sizeof(ret)); 61 ret.mp[0][0]=1; 62 M.qpow(ret,pw,n); 63 ui ans=0; 64 for(int i=0;i<len;i++) 65 (ans+=ret.mp[i][0])%=mod; 66 printf("%u\n",ans); 67 return 0; 68 }
---恢复内容结束---