Luogu P8085 [COCI2011-2012#4] KRIPTOGRAM 题解

题意

给一段明文和一段密文,密文中一个单词对应明文中一个单词(可以密文中多个单词对应明文中同一个单词),求给出的密文在明文中可能出现的最早的位置。

分析

读完题马上想到 kmp,但需要有一些变化。

容易发现两段单词可以匹配当且仅当它们出现的相对顺序相同,如 a a b a b cx 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) dx 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;
}
posted @ 2022-06-08 17:01  l_x_y  阅读(80)  评论(0编辑  收藏  举报