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

浙公网安备 33010602011771号