bzoj 1009 GT考試 (KMP+矩陣乘法)
阿新 • • 發佈:2018-12-11
題目大意:給定一個由數字構成的字串A(len<=20),讓你選擇一個長度為n(n是給定的)字串X,一個合法的字串X被定義為,字串X中不存在任何一段子串與A完全相同,求互不相同的合法的字串L的數量
第一眼看就沒啥思路....瞅了一眼題解,是KMP優化DP,然後再用矩陣優化DP
思路還是不難的,首先用KMP求出原字串的next陣列,再用next轉移
定義f[i][j]是當前X串匹配到了第i位,已經匹配到了字串A的第j位
每次在X串的第j+1位填上一個數c,那麼X串現在最長能匹配上A串的位置
就是從第j+1位一直往前跳next,直到碰到一個位置a[k]==a[j]或k==0也匹配不到
int k=i+1; for(k=i+1;k>0&&a[k]!=c;k=nxt[k]) ; pw.mp[k][i]++;
這是一個連續的過程,上面是構建矩陣的核心程式碼
至於為什麼要這麼跳呢,這是一個類似於"貪心"的過程,但並不是我們主動去貪心
因為我們要保證每次轉移的位置都是正確的
然後發現N<=1e9有點大,矩陣乘法優化一下即可
#include <map> #include <cmath> #include <cstdio> #include <cstring> #include <algorithm> #define ll long long #define N 23 #define ui unsigned int #define inf 0x3f3f3f3f using namespace std; //re int n,len; ui mod; char str[N]; int a[N],nxt[N]; struct mtx{ ui mp[N][N]; friend mtx operator *(const mtx &s1,const mtx &s2) { mtx ret;memset(&ret,0,sizeof(ret)); for(int i=0;i<len;i++) for(int j=0;j<len;j++) for(int k=0;k<len;k++) (ret.mp[i][j]+=(s1.mp[i][k]*s2.mp[k][j])%mod)%=mod; return ret; } mtx qpow(mtx &ans,mtx &x,int y) { while(y){ if(y&1) ans=x*ans; x=x*x;y>>=1; } } }M; void get_kmp() { int i=1,j=0; nxt[1]=0; while(i<=len) if(j==0||a[i]==a[j]) i++,j++,nxt[i]=j; else j=nxt[j]; } int main() { scanf("%d%d%u",&n,&len,&mod); scanf("%s",str+1); for(int i=1;i<=len;i++) a[i]=str[i]-'0'; get_kmp(); mtx pw;memset(&pw,0,sizeof(pw)); for(int i=0;i<len;i++) for(int c=0;c<=9;c++) { if(i==len-1&&a[len]==c) continue; int k=i+1; for(k=i+1;k>0&&a[k]!=c;k=nxt[k]); pw.mp[k][i]++; } mtx ret;memset(&ret,0,sizeof(ret)); ret.mp[0][0]=1; M.qpow(ret,pw,n); ui ans=0; for(int i=0;i<len;i++) (ans+=ret.mp[i][0])%=mod; printf("%u\n",ans); return 0; }