题解
将密文\(t\)反转后,此题便化为一般的字符串匹配。
因为存在对之后状态产生影响的不同方案,可以想到dp:\(dp_i\)表示字符串\(t\)(反转后)前\(i\)项可以匹配的最后一个单词下标(若无法匹配为\(0\))。设单词\(j\)长度为\(a\),如果\(dp[i]>0\)并且存在单词与\(t\)串\([i+1,i+a]\)区间匹配,则\(dp[i+a]=j\)。记录最后单词是为了输出路径,以\(i\)减去最后单词的长度即可找到上一状态。
对于字符串匹配的部分,将\(w\)中的单词存入Trie树中,枚举密文\(t\)中的单词起点在Trie树中查询更新即可。时间复杂度为\(O(nw)\)。
AC代码
#include<bits/stdc++.h>
using namespace std;
const int N=10010,M=1e5+10,W=1e6+10;
char s[N];
int dp[N],t[W][30],qaq[W],cnt,ans[N],tot;
//t:Trie树,qaq[i]:Trie树中编号为i的叶子节点所对单词下标,cnt:Trie树节点个数
string w[M];
int qwq(char x)//字母编号(忽略大小写)
{
if(x>='a' && x<='z') return x-'a';
if(x>='A' && x<='Z') return x-'A';
return 27;
}
void add(int x,int num,int pos)//插入单词
{
if(pos>=w[num].size()) {qaq[x]=num; return;}
int id=qwq(w[num][pos]);
if(!t[x][id]) t[x][id]=++cnt;
add(t[x][id],num,pos+1);
}
void query(int pos,int x)//查询
{
int id=qwq(s[pos]);
dp[pos-1]=max(dp[pos-1],qaq[x]);
if(t[x][id]) query(pos+1,t[x][id]);
}
int main()
{
ios::sync_with_stdio(0);
int n,m;
cin>>n>>s+1>>m; reverse(s+1,s+n+1);
//reverse:反转
for(int i=1;i<=m;i++) {cin>>w[i]; add(0,i,0);}
dp[0]=1;
for(int i=1;i<=n+1;i++)
if(dp[i-1]) query(i,0);
int x=n;
while(x) {ans[++tot]=dp[x]; x=x-w[dp[x]].size();}//回溯路径
for(int i=1;i<=tot;i++) cout<<w[ans[i]]<<" ";
return 0;
}