Luogu P8085 [COCI2011-2012#4] KRIPTOGRAM 题解
题意
给一段明文和一段密文,密文中一个单词对应明文中一个单词(可以密文中多个单词对应明文中同一个单词),求给出的密文在明文中可能出现的最早的位置。
分析
读完题马上想到 kmp,但需要有一些变化。
容易发现两段单词可以匹配当且仅当它们出现的相对顺序相同,如 a a b a b c
和 x x y x y z
,因此可以想到将每个单词用它本次出现的位置与上次出现位置的差来记录它,若第一次出现则记为 inf,如 a a b a b c
可以改写成 inf 1 inf 2 2 inf
。
但是此时如果直接跑普通 kmp 会出现一个问题,如果文本串中这段单词中有一个在整段前出现过,而模式串中对应的单词第一次出现,就会判为不匹配,但有可能是匹配的,例如 c (a a b a b c) d
和 x x y x y z
,可以发现括号中的部分和模式串是匹配的。因此需要在 kmp 时判一下这种匹配的情况,即如果模式串中该单词第一次出现,且文本串中对应单词上次出现的位置在整段前,则也能匹配。
代码
const int MAXN=1e6+7;
string s;
int n,m,a[MAXN],b[MAXN],pi[MAXN];
map<string,int>mp;
int main()
{
int i,j;
for(n=1;cin>>s&&s[0]!='$';n++)
{
if(!mp[s]) mp[s]=n,a[n]=inf;
else a[n]=n-mp[s],mp[s]=n;
}mp.clear();n--;
for(m=1;cin>>s&&s[0]!='$';m++)
{
if(!mp[s]) mp[s]=m,b[m]=inf;
else b[m]=m-mp[s],mp[s]=m;
}m--;pi[1]=0;
for(i=2,j=0;i<=m;i++)
{
while(j>0&&b[i]!=b[j+1]) j=pi[j];
if(b[i]==b[j+1]) j++;pi[i]=j;
}
for(i=1,j=0;i<=n;i++)
{
while(j>0&&(b[j+1]==inf&&j+1-a[i]>0||b[j+1]!=inf&&b[j+1]!=a[i])) j=pi[j];
if(!(b[j+1]==inf&&j+1-a[i]>0||b[j+1]!=inf&&b[j+1]!=a[i])) j++;
if(j==m) return printf("%d\n",i-m+1),0;
}
return 0;
}
本文作者:l_x_y
本文链接:https://www.cnblogs.com/lxy-2022/p/P8085-Solution.html
版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步