QOJ 2273 Suffixes may Contain Prefixes 题解

QOJ 2273 Suffixes may Contain Prefixes 题解


知识点

KMP,exKMP,DP,FSM(DFA,NFA)。

分析

首先建出 \(S\) 的 KMP 自动机,然后考虑在自动机上 DP。

那么设状态为 \(f_{i,u}\) 表示到了 \(i\),在自动机上状态为 \(u\) 的最大值。那么我们需要预处理一个转移值数组 \(val_i\),然后转移时就写成下述形式:

\[\max(f_{i,u}+val_v) \to f_{i+1,v} \\ \]

那么根据题目定义和 KMP 知识,我们知道 \(val_0=0,val_{i} = val_{nxt_i} + 1(i>0)\)

那么我们根据上面最基础的转移式,可以得到 \(O(nm|\Sigma|)\) 的 DFA 方法,就是预处理出 $$ 表示状态 \(u\) 在接受了字符 \(c\) 后,确定地会走到哪个状态,有分类讨论:

\[trs_{u,c} = \begin{cases} u+1 & u<n \land c = s_{u+1} \\ 0 & u=0 \land c \neq s_{u+1} \\ trs_{nxt_u,c} & u\neq 0 \land c \neq s_{u+1} \\ \end{cases} \]

那么 \(O(n|\Sigma|)\) 可以预处理,然后转移就很简单:

\[f_{0,0} = 0\\ \max(f_{i,u}+val_{trs_{u,c}}) \to f_{i+1,trs_{u,c}} \\ \]

如果有学习过 FSM 的知识,那么就知道 NFA 和 DFA 是等价的,而且很多时候 NFA 的状态数更少,跑一遍 NFA 就相当于跑多遍 DFA,所以我们可以考虑将 DFA 转化为 NFA。

其实这题里说转化不太准确,毕竟我们构造的 KMP 自动机是从 NFA 变到 DFA 的:\(nxt\) 是 NFA 转移,而 \(trs\) 是 DFA 转移。

不过上面这种方法没法优化,思考一下就知道了……

网上似乎有很多 \(O(nm)\) 的算法,我们只讲 NFA 的。

考虑换一种形式,用跳 \(i+1\) 来增长长度,贡献在最后结算(失陪时或结束时)。那么就有:

\[f_{i,u} \to f_{i+1,u+1} \\ f_{i,u} + val_u \to f_{i+1,nxt_u} \\ f_{i,u} + val_u \to f_{i+1,nxt_{nxt_u}} \\ \cdots \]

\(O(nm)\) 解决即可。

代码

constexpr int N(2e3+10),C(26);

char s[N];
int n,m,ans;
int f[N],g[N],nxt[N],val[N];

signed main() {
#ifdef Plus_Cat
	freopen(Plus_Cat ".in","r",stdin),freopen(Plus_Cat ".out","w",stdout);
#endif
	/*DE("Input");*/
	scanf("%s",s+1),n=strlen(s+1),I(m);
	/*DE("KMPAM");*/
	int it(-1);
	nxt[0]=-1;
	FOR(u,1,n) {
		while(~it&&s[it+1]!=s[u])it=nxt[it];
		nxt[u]=++it,val[u]=val[nxt[u]]+1;
	}
	nxt[0]=0;
	/*DE("DP");*/
	RCL(f,-INF,f,1),f[0]=0;
	FOR(i,0,m-1) {
		FOR(u,0,n)g[u]=f[u]+val[u];
		DOR(u,n,0) {
			if(u<n)f[u+1]=g[u];
			tomax(g[nxt[u]],g[u]);
		}
	}
	/*DE("Count");*/
	FOR(u,0,n)tomax(ans,f[u]+val[u]);
	/*DE("Output");*/
	O(ans,'\n');
	return 0;
}

posted @ 2025-09-05 14:45  Add_Catalyst  阅读(18)  评论(0)    收藏  举报