bzoj 1009 GT test (KMP + matrix multiplication)

Posted by johnnyk on Fri, 27 Dec 2019 20:11:47 +0100

Given a string a (len < = 20) composed of numbers, let you choose a string x with a length of n(n is given). A legal string x is defined as that there is no segment of string X that is exactly the same as a. find the number of different legal strings L

At the first sight, there is no idea.... after a glance, KMP optimizes DP, and then matrix optimizes DP

The idea is not difficult. First, use KMP to find the next array of the original string, and then use next to transfer

The definition f[i][j] is that the current X string has been matched to the i-th bit, and has been matched to the j-th bit of string A

Fill in A number c in the j+1 bit of X string every time, then the longest x string can match the position of A string

That is, jump forward from the j+1 bit until a position a[k]==a[j] or k==0 is not matched

int k=i+1;
for(k=i+1;k>0&&a[k]!=c;k=nxt[k])
    ;
pw.mp[k][i]++;
    

This is a continuous process. The above is the core code of building matrix (the original code is too ugly, I changed it)

As for why we should jump like this, it's a process similar to "greed", but it's not that we take the initiative to be greedy

Because we need to make sure that every time we move it, it's the right place

Then it is found that n < = 1e9 is a little large, and matrix multiplication can be optimized

#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;
}