BZOJ 1009 KMP思想 + DP + 矩阵快速幂

来源:互联网 发布:网络新技术专题报告 编辑:程序博客网 时间:2024/06/08 14:48

第一次用MarkDown和LaTex,写得有点丑……

本题的坑爹历程给了我一个血的教训:没有真正搞清楚做法之前,不要瞎BB地写题解。不然会造成深陷坑中的严重后果

题意简述:给定一个字符串s,求出长度为n的不含字串s的字符串t的数量。

这道题是一个非常经典的模型,DP之:
 f[i][j]为前i个t字符,匹配到s的第 j位(强制选 i)的方案数,则有

ans=Σ( f[n][k] | 0k<m )

接下来考虑 f[i][j]的转移:
由于我们是要统计数量,所以我们可以枚举第 i+1位是几。然后自然地,应该转移到(这里亦即贡献到)f[i+1][pos],其中pos为加上这个字符之后,当前串匹配到的位置。

然后我们发现,每一轮i=>i+1的转移都是一样的。对于这样的“无脑”递推,我们可以构造矩阵,然后使用矩阵快速幂来加速。如何构造矩阵呢?我们考虑矩阵元素M[i][j]的意义。它会与当前向量的第j个维度相乘,然后贡献到下一个向量的第i个维度。因此,如果有f[i+1][pos] += f[i][j],就应把M[pos][j]加1。不难发现,处理好这个矩阵之后,它的第0列其实就是初始向量。所以,只需做n次矩阵乘法即可。

代码略丑:

// BZOJ 1009 KMP+Matrix#include <cstdio>#include <cstring>#include <algorithm>using namespace std; const int M=25, N=M; #define rep(i,a,b) for (int i=a; i<=b; i++) #define dep(i,a,b) for (int i=a; i>=b; i--) #define read(x) scanf("%d", &x) #define fill(a,x) memset(a, x, sizeof(a)) int mod; struct Matrix {    int n, m, a[N][M];    void init(int n, int m) {        this->n=n;        this->m=m;        fill(a, 0);    }    Matrix operator * (const Matrix B) const {          Matrix ret;        ret.init(B.n, B.n);        rep(i,0,n-1) rep(j,0,n-1) rep(k,0,n-1) ret.a[i][j]=(ret.a[i][j]+a[i][k]*B.a[k][j])%mod;        return ret;     }    Matrix operator ^ (const int k) const {        Matrix ret, t=*this;        ret.n=n; ret.m=m;        int tk=k;        fill(ret.a, 0);        rep(i,0,ret.n-1) ret.a[i][i]=1;        while (tk) {            if (tk&1) ret=ret*t;            t=t*t;            tk>>=1;             }        return ret;    } } A; int f[M], n, m; char st[M]; void get_fail() {    f[0]=f[1]=0;    int j=0;    rep(i,2,m) {        while (j && st[j+1]!=st[i]) j=f[j];        if (st[j+1]==st[i]) j++;        f[i]=j;    } }int main(){    read(n); read(m); read(mod);    scanf("%s", st+1);    A.init(m, m);    get_fail();    rep(i,0,m-1)       rep(j,0,9) {        int pos=i;         while (pos && st[pos+1]-'0'!=j) pos=f[pos];        if (st[pos+1]-'0'==j) pos++;        if (pos!=m) A.a[pos][i]=(A.a[pos][i]+1)%mod;      }    A=A^n;    int ans=0;    rep(i,0,m-1) ans=(ans+A.a[i][0])%mod;    printf("%d\n", ans);    return 0;}
0 0
原创粉丝点击