LightOJ 1268 Unlucky Strings(KMP+矩陣乘法+基礎DP)
阿新 • • 發佈:2019-01-01
題意
給出字串的長度 \(n\) ,以及該字串是由哪些小寫字母組成,現給出一個壞串 \(S\) ,求存在多少種不同的字串,使得其子串不含壞串。
\(1 \leq n \leq 10^9\)
\(1 \leq |S| \leq 50\)
思路
矩陣快速冪優化 \(\text{dp}\) 是真的常見,在同層狀態數不多,但層數很多的時候,要考慮矩陣快速冪優化 \(\text{dp}\) 。
每一層的狀態 \(dp[i]\) 表示匹配到哪裡,再列舉給定的字母進行轉移,只要不匹配到 \(S\) 結尾都是一個合法的轉移,轉移係數為 \(1\) 。剩下就是板子了。
程式碼
#include<bits/stdc++.h> #define FOR(i,x,y) for(int i=(x),i##END=(y);i<=i##END;++i) #define DOR(i,x,y) for(int i=(x),i##END=(y);i>=i##END;--i) typedef long long LL; typedef unsigned int uint; using namespace std; const int N=53; struct Matrix { int n,m;uint a[N][N]; uint *operator [](const int x){return a[x];} void resize(int _n,int _m){n=_n,m=_m;} Matrix operator *(const Matrix &_)const { Matrix res;res.resize(n,_.m); FOR(i,1,n)FOR(j,1,_.m) { res[i][j]=0; FOR(k,1,m)res[i][j]+=a[i][k]*_.a[k][j]; } return res; } Matrix operator *=(const Matrix &_){return (*this)=(*this)*_;} }A,B; char d_c[30],str[N]; int c_d[256]; int f[N],F[N][30]; int n,m,l; Matrix Pow(Matrix a,int p) { Matrix res;res.resize(a.n,a.n); FOR(i,1,res.n)FOR(j,1,res.n)res[i][j]=(i==j); for(;p>0;p>>=1,a*=a)if(p&1)res*=a; return res; } int main() { int Case; scanf("%d",&Case); FOR(cas,1,Case) { scanf("%d",&n); scanf("%s",d_c+1); scanf("%s",str+1); l=strlen(d_c+1); m=strlen(str+1); FOR(i,1,l)c_d[(int)d_c[i]]=i; f[1]=f[2]=1;FOR(i,1,l)F[1][i]=1+(i==c_d[(int)str[1]]); FOR(i,2,m) { f[i+1]=F[f[i]][c_d[(int)str[i]]]; FOR(j,1,l) { if(c_d[(int)str[i]]==j)F[i][j]=i+1; else F[i][j]=F[f[i]][j]; } } A.resize(1,m),B.resize(m,m); FOR(i,1,m)A[1][i]=0; FOR(i,1,m)FOR(j,1,m)B[i][j]=0; A[1][1]=1; FOR(i,1,m)FOR(j,1,l)if(F[i][j]<=m)B[i][F[i][j]]++; A*=Pow(B,n); uint ans=0; FOR(i,1,m)ans+=A[1][i]; printf("Case %d: %u\n",cas,ans); } return 0; }