1. 程式人生 > >BZOJ1009 單模板自動機 矩陣快速冪優化DP

BZOJ1009 單模板自動機 矩陣快速冪優化DP

第一次在BZOJ自己做出不是那麼水的題,但是看這過題人數。。。嘛還是寫一下題解吧

題目大意:求只由0到9組成的不包含m(m<=20)位模式串的n(n<=1e9)位字串的個數

涉及到字串匹配的話 很自然應該往KMP和自動機的方向想。
然後可以很容易發現建自動機可以得到一個DP做法:
定義dp[i][j]為前i位到達狀態j的方案數
那麼對每個next[j][k]!=m,
都有dp[i+1][next[j][k]]+=dp[i][j] ,
那麼答案就是dp[n][i]
然後n很大 那麼就矩陣快速冪吧
至此問題完美解決!

程式碼:

#include<iostream>
#include<cstdio> #include<cstdlib> #include<cstring> #include<cmath> #include<algorithm> #include<string> #include<iomanip> #include<vector> #include<set> #include<map> #include<queue> using namespace std; typedef long long LL; typedef
unsigned long long ULL; #define rep(i,k,n) for(int i=(k);i<=(n);i++) #define rep0(i,n) for(int i=0;i<(n);i++) #define red(i,k,n) for(int i=(k);i>=(n);i--) #define sqr(x) ((x)*(x)) #define clr(x,y) memset((x),(y),sizeof(x)) #define pb push_back const int maxn=100; const int maxs=10; int n,m,mod; int
nxt[maxn][maxs],fail[maxn]; int root,tot; int newnode() { for(int i=0;i<maxs;i++)nxt[tot][i]=-1; return tot++; } void init() { tot=0; root=newnode(); } void insert(char str[]) { int len=strlen(str); int now=root; for(int i=0;i<len;i++) { char s=str[i]-'0'; if(nxt[now][s]==-1) nxt[now][s]=newnode(); now=nxt[now][s]; } } void build() { queue<int> q; fail[root]=root; for(int i=0;i<maxs;i++) { if(nxt[root][i]==-1) nxt[root][i]=root; else { fail[nxt[root][i]]=root; q.push(nxt[root][i]); } } while(!q.empty()) { int now=q.front(); q.pop(); for(int i=0;i<maxs;i++) { if(nxt[now][i]==-1) nxt[now][i]=nxt[fail[now]][i]; else { fail[nxt[now][i]]=nxt[fail[now]][i]; q.push(nxt[now][i]); } } } } struct MAT { int a[25][25]; int n; void init(int x,int m) { clr(a,0); n=m; if(x)rep0(i,n)a[i][i]=1; } MAT operator * (const MAT &x) const { MAT ret;ret.init(0,n); rep0(i,n)rep0(j,n)rep0(k,n)(ret.a[i][j]+=a[i][k]*x.a[k][j]%mod)%=mod; return ret; } void add(int x,int y) { a[x][y]++; } }; MAT quipow(MAT x,LL k) { MAT ret;ret.init(1,x.n); while(k) { if(k&1)ret=ret*x; x=x*x; k>>=1; } return ret; } char str[30]; int main() { scanf("%d%d%d",&n,&m,&mod); scanf("%s",str); init(); insert(str); build(); MAT A;A.init(0,m); rep0(i,m) { rep0(j,10) { if(nxt[i][j]!=m) { A.add(nxt[i][j],i); } } } MAT B=quipow(A,n); int ans=0; rep0(i,m)ans+=B.a[i][0]; printf("%d\n",ans%mod); return 0; }