【集训】KMP泛做

KMP泛做

  • KMP泛做
    • 链接
    • ProblemA HDU 4300
    • ProblemB HDU 2087
    • ProblemC HDU 4763
    • ProblemF HDU 2594
    • ProblemH HDU 3746


链接

  • 集训KMP

    只写了部分,D题我调不出来,谁有兴趣可以帮我调   :


ProblemA (HDU 4300)

  • Clairewd’s message

    大意是给你两个串,一个串是密码表,另外一个串是密文+明文(明文可能不全),现在让你补全明文。
    注意到明文翻译过来就是密文,所以先把串翻译一遍,得到“****+密文”,(*代表密文再翻译的部分)这样就发现原串和当前的串有相同的部分(密文),所以我们只需要求得当前串后缀和原串前缀相同的部分就可以了(题中说了不相互覆盖)。所以利用推next数组的方法,对2个串求一次next数组即可。
    可以看到在函数中就是改了其中一个数组名而已。

#include 
#include 
#include 
#define maxn 100005

using namespace std;

int T,Next[maxn];
char s1[maxn],s2[maxn],s3[maxn],key[maxn];

void getnext(int l)
{
    memset(Next,0,sizeof(Next));
    Next[0]=-1; Next[1]=0;
    for (int i=1;iint k=Next[i];
        while (k!=-1)
        {
            if (s3[i]==s2[k])
            {
                Next[i+1]=k+1;
                break;
            }
            else k=Next[k];
        }
    }
}

int main()
{
    scanf("%d",&T);
    while (T--)
    {
        memset(s1,0,sizeof(s1));
        memset(s2,0,sizeof(s2));
        memset(s3,0,sizeof(s3));
        scanf("%s",s1);
        scanf("%s",s2);
        for (int i=0;i<26;i++) key[s1[i]-'a']=i+'a';
        int l=strlen(s2);
        for (int i=0;i'a'];
        getnext(l);
        int k=Next[l];
        if (k>l/2) k=l/2;
        for (int i=0;iprintf("%c",s2[i]);
        for (int i=0;iprintf("%c",key[s2[i]-'a']);
        printf("\n");
    }
    return 0;
}

ProblemB (HDU 2087)

  • 剪花布条

    这个就是个完全裸的KMP了,直接匹配就好。

#include 
#include 
#include 
#define maxn 1005

using namespace std;

char s1[maxn],s2[maxn];
int l1,l2,Next[maxn];

void getnext(int l,char s[])
{
    memset(Next,0,sizeof(Next));
    Next[0]=-1; Next[1]=0;
    for (int i=1;iint k=Next[i];
        while (k!=-1)
        {
            if (s[i]==s[k])
            {
                Next[i+1]=k+1;
                break;
            }
            else k=Next[k];
        }
    }
}

int main()
{
    while (scanf("%s",s1),s1[0]!='#')
    {
        scanf("%s",s2);
        l1=strlen(s1); l2=strlen(s2);
        getnext(l2,s2);
        int i=0,j=0,ans=0;
        while (iwhile (s1[i]==s2[j] && jif (j==0) i++;
            if (j==l2)
            {
                ans++;
                j=0;
            }
            else j=Next[j];
        }
        printf("%d\n",ans);
    }
    return 0;
}

ProblemC (HDU 4763)

  • Theme Section

    给你一个字符串,要你求它是否满足“E A E B E”这样的格式,如果满足,需要你求“E”部分的最长长度。
    可以发现前面和后面都有E,主要麻烦的是求中间的E,简单起见,我们先对串求一次next,求完之后根据next删去首尾,然后用删去的部分和余下的部分匹配,如果匹配成功就直接返回结果,否则就缩小next继续匹配(j=next[j])

#include 
#include 
#include 
#define maxn 1000005

using namespace std;

int T,Next[maxn];
char s[maxn],p[maxn];

void getnext(int l,char s[])
{
    memset(Next,0,sizeof(Next));
    Next[0]=-1; Next[1]=0;
    for (int i=1;iint k=Next[i];
        while (k!=-1)
        {
            if (s[i]==s[k]) {Next[i+1]=k+1; break;}
            else k=Next[k];
        }
    }
}

int main()
{
    scanf("%d",&T);
    while (T--)
    {
        scanf("%s",s);
        int l=strlen(s);
        getnext(l,s);
        int k=Next[l];
        bool flag=0;
        int ans=0;
        while (k>l/2) k=Next[k];
        while (k!=-1)
        {
            memset(p,0,sizeof(p));
            for (int i=0;iint lp=k,i=k,j=0;
            while (iwhile (s[i]==p[j] && jif (j==0)
                {
                    i++;
                    continue;
                }
                if (j==lp && j!=0) {flag=1; ans=k; break;}
                else j=Next[j];
            }
            if (flag) break;
            k=Next[k];
        }
        printf("%d\n",ans);
    }
    return 0;
}

ProblemF (HDU 2594)

  • Simpsons’ Hidden Talents

    给你两个串,要你求两个串的next(简单吧)。
    把两个串dou起来求就行了(这个题数据好像很弱,一开始我交了个错的都过了)。

#include 
#include 
#include 
#define maxn 50005

using namespace std;

int T,Next[maxn*2];
char s1[maxn],s2[maxn],s3[maxn*2];

int getnext(int l,char s[])
{
    memset(Next,0,sizeof(Next));
    Next[0]=-1; Next[1]=0;
    for (int i=1;iint k=Next[i];
        while (k!=-1)
        {
            if (s[i]==s[k])
            {
                Next[i+1]=k+1;
                break;
            }
            else k=Next[k];
        }
    }
    return Next[l];
}

int main()
{
    while (scanf("%s%s",s1,s2)!=EOF)
    {
        int l1=strlen(s1),l2=strlen(s2);
        for (int i=0;ifor (int i=0;iint k=getnext(l1+l2,s3);
        if (!k) printf("0\n");
        else
        {
            while (k>min(l1,l2)) k=Next[k];
            for (int i=0;iprintf("%c",s3[i]);
            printf(" %d\n",k);
        }
    }
    return 0;
}

ProblemH (HDU 3746)

  • Cyclic Nacklace

    给你一个串,要让这个串中至少有2个循环节,问现在应该加多少个字母(左右都可以加字母)
    因为是一个环,所以在左边加和在右边加是一样的,通过画图分析:

  1. 一个串中如果有多个循环节, nextl 一定大于串的一半长。
  2. 一个串中如果没有循环节,加的字母一定等于 l2nextl

    主要是第一个比较难求,不过也可以通过画图发现前缀后缀重叠的部分就是循环节多出来的长度,那么我们可以通过这个先求出循环节的长度,然后再求出整个串需要多加多少。

#include 
#include 
#include 
#define maxn 100015

using namespace std;

int T,Next[maxn];
char s[maxn];

int getnext(int l,char s[])
{
    memset(Next,0,sizeof(Next));
    Next[0]=-1; Next[1]=0;
    for (int i=1;iint k=Next[i];
        while (k!=-1)
        {
            if (s[i]==s[k])
            {
                Next[i+1]=k+1;
                break;
            }
            else k=Next[k];
        }
    }
    return Next[l];
}

int main()
{
    scanf("%d",&T);
    while (T--)
    {
        scanf("%s",s);
        int l=strlen(s),cir,k;
        k=getnext(l,s);
        if (k>l/2) printf("%d\n",(l-k-l%(l-k))%(l-k)   );
        else printf("%d\n",l-k*2);
    }
    return 0;
}

你可能感兴趣的:(水水更健康,KMP)