P3193 [HNOI2008]GT考試 題解(kmp+矩陣快速冪)
阿新 • • 發佈:2021-07-21
題目連結
題目思路
設\(dp[i][j]\)表示長度為\(i\)的字串匹配長度為\(j\)的字串
如果第\(i+1\)個字元等於\(s[j+1]\) 那麼就轉移到\(dp[i+1][j+1]\)
否則根據\(kmp\)那麼此時\(j=nxt[j]\) 然後再遞迴判斷
資料這麼大要往矩陣快速冪的思路去思考
設\(base.a[i][j]\) 表示有多少種方案數使得加一個字元使得匹配\(i\)個字元變為\(j\)個字元
那麼\(ans.a[i][j]=\sum_{k=0}^{k=m-1}ans.a[i][k]\times base.a[k][j]\)
然後再使用矩陣快速冪加速
對於矩陣快速冪的理解
以我們把每一行\(ans[i][j]\)抽象成只有一行,列數為\(m-1\)的\(F[i]\)
最後即為\(F[i]=F[i-1]*base^n\)
我感覺這個題目太多細節了
實在太妙了
程式碼
卷也卷不過,躺又躺不平#include<bits/stdc++.h> using namespace std; typedef long long ll; typedef unsigned long long ull; //typedef pair<int,int> pii; #define fi first #define se second #define debug printf("aaaaaaaaaaa\n"); const int maxn=20+5,inf=0x3f3f3f3f; const ll INF=0x3f3f3f3f3f3f3f3f; int n,m,k,mod; char s[maxn]; int nxt[maxn]; struct matrix{ ll a[maxn][maxn]; }base,ans,last; matrix mul(matrix a,matrix b,int n){ matrix temp; memset(temp.a,0,sizeof(temp.a)); //一定1要清空 for(int i=0;i<n;i++){ for(int j=0;j<n;j++){ for(int k=0;k<n;k++){ temp.a[i][k]+=(a.a[i][j])*(b.a[j][k]); temp.a[i][k]%=mod; } } } return temp; } void qpow(ll n,ll b){ while(b){ if(b&1){ ans=mul(ans,base,n); } base=mul(base,base,n); b=b>>1; } } void getnext(){ for(int i=2,j=0;i<=m;i++){ while(j&&s[j+1]!=s[i]){ j=nxt[j]; } if(s[j+1]==s[i]) j++; nxt[i]=j; } } void getbase(){ for(int i=0;i<m;i++){ //現在匹配了i位 for(int j=0;j<=9;j++){ int x=i; while(x&&j!=s[x+1]-'0'){ x=nxt[x]; } if(j==s[x+1]-'0'){ x++; } if(x!=m){ base.a[i][x]=(base.a[i][x]+1)%mod; } } } } int main(){ scanf("%d%d%d",&n,&m,&mod); scanf("%s",s+1); getnext(); getbase(); for(int i=0;i<m;i++){ ans.a[i][i]=1; } qpow(m,n); ll pr=0; last.a[0][0]=1; last=mul(last,ans,m); for(int i=0;i<m;i++){ pr=(pr+last.a[0][i])%mod; } printf("%lld\n",pr); return 0; }