「一本通 6.5 练习 2」GT 考试

「一本通 6.5 练习 2」GT 考试

题目大意

给你一个n,你需要枚举n位的数字,其中不能出现子串=t,问你这样的数字可以有多少个,最后的答案mod p

分析

这个题很容易想到dp, d p [ i ] [ j ] dp[i][j] dp[i][j]代表n位数字代表的文本串s前i位的后j位与模式串t的前j位匹配上的方案数.

那我们最后的答案就是 ∑ j = 0 m − 1 d p [ n ] [ j ] \sum_{j=0}^{m-1}{dp[n][j]} j=0m1dp[n][j]

分析状态转移: d p [ i ] [ j ] dp[i][j] dp[i][j]的状态转移到下一个状态的情况可能有

  • d p [ i + 1 ] [ k ] ( 0 &lt; = k &lt; = j ) dp[i+1][k] (0&lt;=k&lt;=j) dp[i+1][k](0<=k<=j) 文本串的第i+1位与模式串的j+1位没有匹配上,所以能匹配的位置跳到了模式串前面具有相同前缀的地方(kmp)

  • d p [ i + 1 ] [ j + 1 ] dp[i+1][j+1] dp[i+1][j+1] 文本串的第i+1位与模式串的j+1位匹配上了

换句话来说, d p [ i ] [ j ] dp[i][j] dp[i][j]的状态来源可能是i-1位匹配成功( d p [ i − 1 ] [ j − 1 ] dp[i-1][j-1] dp[i1][j1]) 或者i-1匹配到某个位置k ( d p [ i − 1 ] [ k ] dp[i-1][k] dp[i1][k]) i位置失配,然后跳回了j

而且这个跳是与i无关的,可以利用这个性质降低复杂度. 定义二维数组 b [ i ] [ j ] b[i][j] b[i][j]。含义是假如现在前i位匹配成功了, 后面再加上一个数字进行匹配, 会变成了匹配到第j位,这里的的 b [ i ] [ j ] b[i][j] b[i][j]求法类似于nxt求法(多综合了一种情况, 这里的i<j是存在的,即j位匹配成功了,j+1位又匹配成功了, 那么 b [ j ] [ j + 1 ] + + b[j][j+1]++ b[j][j+1]++)

那么 d p [ i ] [ j ] = ∑ k = 0 m − 1 d p [ i − 1 ] [ k ] ∗ b [ k ] [ j ] dp[i][j]=\sum_{k=0}^{m-1}{dp[i-1][k]*b[k][j]} dp[i][j]=k=0m1dp[i1][k]b[k][j]

这里的乘积和运算与矩阵的运算是类似的,循环范围不变, 后面乘数不变, 可以先计算后面n个 b [ k ] [ j ] b[k][j] b[k][j]得到 ans矩阵, 再初始状态*ans得到答案,

//dp+kmp+矩阵快速幂
#include <iostream>
#include <stdio.h>
#include <stdlib.h>
#include <cmath>
#include <string>
#include <cstring>
#include <algorithm>
using namespace std;
#define ll long long
#define ull unsigned long long
#define pi acos(-1)
const int inf=0x3f3f3f3f;
const int N=505;
const int length=25;
ll n,m,p;
char s[N];
int nxt[N];
struct node{
    
    
    ll matrix[length+5][length+5]={
    
    0};
    node(int x=0){
    
    
        for(int i=0;i<length;i++){
    
    
            matrix[i][i]=x;
        }
    }
};
node mul(node a,node b){
    
    
    node ans;
    for(int i=0;i<m;i++){
    
    
        for(int j=0;j<m;j++){
    
    
            for(int k=0;k<m;k++){
    
    
                ans.matrix[i][j]=(ans.matrix[i][j]+a.matrix[i][k]*b.matrix[k][j]%p)%p;
            }
        }
    }
    return ans;
}
node pow(node a,ll k){
    
    
    node ans(1);
    while(k){
    
    
        if(k&1){
    
    
            ans=mul(ans,a);
        }
        k>>=1;
        a=mul(a,a);
    }
    return ans;
}
int main() 
{
    
    
#ifdef __DEBUG__
	freopen("in.txt","r",stdin);
	freopen("out.txt","w",stdout);
#endif
	scanf("%lld%lld%lld",&n,&m,&p);
	scanf("%s",s+1);
	node ans,base;
	nxt[1]=0;
	int pre=0,slen=strlen(s+1);
	for(int i=2;i<=slen;i++){
    
    //kmp求最大匹配长度
		while(pre&&s[pre+1]!=s[i])pre=nxt[pre];
		if(s[pre+1]==s[i]){
    
    
			++pre;
		}
		nxt[i]=pre;
	}
	for(int i=0;i<m;i++){
    
    
		for(int j=0;j<=9;j++){
    
    //枚举可能出现的任意数字
			pre=i;//这里的赋值导致了i<j可行
			while(pre&&s[pre+1]!=j+'0')pre=nxt[pre];
			if(s[pre+1]==j+'0'){
    
    
				++pre;
			}
			if(i!=m){
    
    //去掉非法状态,我们不需要全部能匹配上的子串
				base.matrix[i][pre]=(base.matrix[i][pre]+1)%p;
			}//i跳到pre 的方案数++
		}
	}
	ans.matrix[0][0]=1;//初始化条件
	ans=mul(ans,pow(base,n));//初始状态*矩阵快速幂的结果
	ll sum=0;
	for(int i=0;i<m;i++){
    
    //循环到m-1
		sum=(sum+ans.matrix[0][i])%p;
	}
	printf("%lld\n",sum);
	return 0;
}

猜你喜欢

转载自blog.csdn.net/jianglw1/article/details/98247505