• 「CTS2019 | CTSC2019」重复(Kmp)


    「CTS2019 | CTSC2019」重复(Kmp)


    Part1

    首先我们考虑对于一个已经确定的\(t\)串,如何检查是否合法

    对于\(s\)串建立\(\text{kmp}\)(\(\text{kmp}\)自动机当然可以),

    如果当前\(\text{kmp}\)指针\(j\)\(\text{fail}\)树上的祖先所对应的所有下一个位置\(s[ancestor+1]\)中,存在一个字符,\(t\)中当前位置的字符\(t[i]<s[ancestor+1]\)

    那么就是存在一个"有意义的串",并且这个串和s串第一个不同的位置就是\(ancestor+1\)

    所以可以预处理一个\(fail\)树上祖先最大的\(s[ancestor+1],Max[state]\)

    rep(i,1,n) Max[i-1]=s[i];
    Max[n]='a';//边界条件
    cmax(Max[1],Max[0]);
    rep(i,2,n) {
    	int j=nxt[i-1];
    	while(j && s[i]!=s[j+1]) j=nxt[j];
    	if(s[i]==s[j+1]) ++j;
    	nxt[i]=j;
    	cmax(Max[i],Max[j]);
    }
    //预处理Max
    rep(i,0,n) {
    	if(i<n) Trans[i][s[i+1]-'a']=i+1;
    	rep(j,0,25) if(j!=s[i+1]-'a') Trans[i][j]=Trans[nxt[i]][j];
    }//kmp自动机,不要再暴力了
    rep(i,m+1,n+m) t[i]=t[i-m]; // 延长到n+m就够了
    int j=0,f=0;
    rep(i,1,n+m){
    	if(t[i]<Max[j]) { // 存在一个位置满足即可
    		f=1;
    		break;
    	}
    	j=Trans[j][t[i]-'a'];
    }
    

    配合dfs枚举,可以拿到pts10


    Part2

    设状态转移为\(Trans[state][c]\)

    把kmp状态压进dp状态里

    如果把问题直接放到无穷意义下来看

    那么跑够长度之后,后面的任意加入\(m\)次字符都会构成一个循环

    枚举循环开始状态为\(st\)\(\text{dp}\)走了\(m\)步回到\(st\)的方案数

    如果计算合法方案数,那么还要多一维,所以直接计算不合法方案,也就是

    \(Trans[state][Max[state]..25]\)这一段转移是不会出现合法情况的

    最后减一下

    复杂度\(O(m\cdot n^2)\)

    namespace pt2{
    	int dp[2][N];
    	void Solve(){
    		int cur=0,ans=0;
    		rep(st,0,n) { // 枚举初始状态
    			rep(i,0,n) dp[cur][i]=dp[!cur][i]=0;
    			dp[cur][st]=1;
    			rep(t,1,m) {
    				cur^=1;
    				rep(i,0,n) if(dp[!cur][i]){
    					rep(c,Max[i]-'a',25) {//只转移不合法的
    						dp[cur][Trans[i][c]]+=dp[!cur][i];
    						Mod1(dp[cur][Trans[i][c]]);
    					}
    					dp[!cur][i]=0;
    				}
    			}// 走m步回到st
    			ans+=dp[cur][st],Mod1(ans);
    		}
    		ans=(qpow(26,m)-ans+P)%P;//减一下
    		printf("%d\n",ans);
    	}
    }
    
    
    

    Part3

    观察上面一档情况,合法的转移\(Trans[state][Max[state]..25]\)

    如果枚举下一个字符\(c>Max[state]\),那么在\(s\)串中就找不到任何匹配了,下一个状态就是\(0\)

    否则,下一个状态就是\(Trans[state][c]\)

    也就是说,每个点出发其实只有两种情况,一种是一定会经过\(0\)

    所以对于这个环是否经过\(0\)进行讨论

    如果不经过\(0\),那么考虑直接从\(st\)出发走\(m\)步非0转移即可

    经过\(0\)的,预处理一个从\(0\)出发,走了\(i\)步第一次回到\(0\)的方案数

    \([st\rightarrow 0,0\rightarrow 0,0\rightarrow 0...,0\rightarrow st]\)

    长成这个样子的转移

    枚举第一个\(st\rightarrow 0\)的时间,后面可以预处理出来

    复杂度\(O(n\cdot m)\)

    #include<bits/stdc++.h>
    using namespace std;
    
    #define pb push_back
    #define reg register
    typedef long long ll;
    #define Mod1(x) ((x>=P)&&(x-=P))
    #define Mod2(x) ((x<0)&&(x+=P))
    #define rep(i,a,b) for(int i=a,i##end=b;i<=i##end;++i)
    #define drep(i,a,b) for(int i=a,i##end=b;i>=i##end;--i)
    
    template <class T> void cmin(T &a, T b){ ((a>b)&&(a=b)); }
    template <class T> void cmax(T &a, T b){ ((a<b)&&(a=b)); }
    
    
    char IO;
    template <class T=int> T rd(){
    	T s=0; int f=0;
    	while(!isdigit(IO=getchar())) if(IO=='-') f=1;
    	do s=(s<<1)+(s<<3)+(IO^'0');
    	while(isdigit(IO=getchar()));
    	return f?-s:s;
    }
    
    const int N=2010,P=998244353;
    
    int n,m;
    char s[N];
    int Max[N],nxt[N],Trans[N][26];
    ll qpow(ll x,ll k=P-2) {
    	ll res=1;
    	for(;k;k>>=1,x=x*x%P) if(k&1) res=res*x%P;
    	return res;
    }
    
    int st[N][N]; // 从i出发,走了j步第一次到0
    int dp[N][N]; // 从0出发,走了i步,到了j,如果到0就不进行转移 
    
    int main(){
    	freopen("repeat.in","r",stdin),freopen("repeat.out","w",stdout);
    	m=rd();
    	scanf("%s",s+1),n=strlen(s+1);
    	rep(i,1,n) Max[i-1]=s[i];
    	Max[n]='a';
    	cmax(Max[1],Max[0]);
    	rep(i,2,n) {
    		int j=nxt[i-1];
    		while(j && s[i]!=s[j+1]) j=nxt[j];
    		if(s[i]==s[j+1]) ++j;
    		nxt[i]=j;
    		cmax(Max[i],Max[j]);
    	}
    	rep(i,0,n) {
    		if(i<n) Trans[i][s[i+1]-'a']=i+1;
    		rep(j,0,25) if(j!=s[i+1]-'a') Trans[i][j]=Trans[nxt[i]][j];
    	}
    
    	int ans=0;	
    	st[0][0]++;
    	rep(i,1,n) {
    		int j=i,f=1;
    		rep(k,1,m) {
    			st[i][k]+=25-(Max[j]-'a'),j=Trans[j][Max[j]-'a'];
    			if(!j) {
    				st[i][k]++,f=0;
    				break;
    			}
    		}
    		if(j==i) ans+=f;
    	}
    
    	dp[0][0]=1;
    	rep(i,1,m) {
    		rep(j,0,n) if(dp[i-1][j]) {
    			dp[i][Trans[j][Max[j]-'a']]+=dp[i-1][j],Mod1(dp[i][Trans[j][Max[j]-'a']]);
    			dp[i][0]=(dp[i][0]+1ll*(25-(Max[j]-'a'))*dp[i-1][j])%P;
    		}
    	}
    
    	rep(i,0,n) { // 枚举初始状态
    		rep(j,0,m) if(st[i][j] && dp[m-j][i]) { // 枚举走了几步第一次到0
    			ans=(ans+1ll*st[i][j]*dp[m-j][i])%P;
    		}
    	}
    	ans=(qpow(26,m)-ans+P)%P;
    	printf("%d\n",ans);
    }
    
    
    
    
    
    
    
    
    
    
    
  • 相关阅读:
    mybatis-plus 错误 java.lang.NoClassDefFoundError
    MySQL+navicat-1064 Error解决方案
    cnblogs博客园修改网站图标icon
    python+pycharm+PyQt5 图形化界面安装教程
    vuex的安装与使用
    vue-router的安装和使用
    VUE-CLI3如何更改配置
    VUE-CL3创建项目
    VUE-CLI2的初始化项目过程
    vuecli脚手架的安装与配置
  • 原文地址:https://www.cnblogs.com/chasedeath/p/12936536.html
Copyright © 2020-2023  润新知