山东济南彤昌机械科技有限公司 山东济南江鹏工贸游有限公司

bzoj 1009 [HNOI2008]GT考试(DP+KMP+矩阵乘法)

 

【题目链接】

 

    http://www.lydsy.com/JudgeOnline/problem.php?id=1009

 

【题意】

 

    给定一个字符串T,问长度为n且不包含串T的字符串有多少种。

 

【思路】

 

    设长度为i的串与T匹配长度为j,有转移式如下:

        f[i+1][j+1]+=f[i][j]

        f[i+1][k]+=f[i][j]

   第一种是匹配成功,第二种是匹配失败。注意如果匹配失败匹配长度并不一定变为0,考虑如果匹配失败f[i][j]可以转移到哪,假设新字符为c,则可以用KMP算法预处理出fail数组,从而计算出应该转移到的位置pos。

    考虑到n比较大,而f的计算又是有规律的,我们采用矩阵乘法优化DP。

    如果i可以转移到pos,则在转移矩阵A中使A[i][pos]++,代表f[cur][pos]的计算需要累加一次f[cur-1][i]。

    注意程序中的fail[i]代表的是i刚好与fail[i]匹配。

 

【代码】

 

 1 #include<cstdio>
 2 #include<cstring>
 3 #include<iostream>
 4 #define FOR(a,b,c) for(int a=b;a<=c;a++)
 5 using namespace std; 
 6 
 7 typedef long long ll;
 8 const int maxn = 25;
 9 
10 char s[maxn];
11 int f[maxn],MOD,n,m,K;
12 
13 struct Matrix {
14     int r,c; 
15     ll N[maxn][maxn];
16     void init(int r,int c) {
17         this->r=r,this->c=c;
18         memset(N,0,sizeof(N));
19     }
20     Matrix operator * (const Matrix B) const {
21         Matrix C; C.init(r,B.c);
22         for(int i=0;i<r;i++)
23             for(int j=0;j<B.c;j++) 
24                 for(int k=0;k<c;k++)
25                     C.N[i][j]=(C.N[i][j]+(ll)N[i][k]*B.N[k][j])%MOD;
26         return C;
27     }
28     Matrix Pow(int p) {
29         Matrix tmp=*this,ans;
30         ans.init(r,r);
31         for(int i=0;i<r;i++) ans.N[i][i]=1;
32         while(p) {
33             if(p&1) ans=ans*tmp;
34             tmp=tmp*tmp; p>>=1;
35         }
36         return ans;
37     }
38 }A;
39 
40 void get_fail()                        //所构造fail 意为i与f[i]处匹配 
41 {
42     int j=0;
43     for(int i=2;i<m;i++) {
44         while(j&&s[j+1]!=s[i]) j=f[j];
45         if(s[j+1]==s[i]) j++;
46         f[i]=j;
47     }
48 }
49 
50 int main()
51 {
52     scanf("%d%d%d%s",&n,&m,&MOD,s+1);
53     get_fail();
54     A.init(m+1,m+1);
55     FOR(i,0,m-1)
56         FOR(j,0,9) {
57             int x=i;
58             while(x&&s[x+1]-'0'!=j) x=f[x];
59             if(j==s[x+1]-'0') A.N[i][x+1]++;
60             else A.N[i][0]++;
61         }
62     A=A.Pow(n);
63     ll ans=0;
64     FOR(i,0,m-1) ans=(ans+A.N[0][i])%MOD;
65     printf("%lld\n",ans);
66     return 0;
67 }

 

posted on 2016-03-29 17:09  hahalidaxin  阅读(191)  评论(0编辑  收藏  举报