继KB用TRIE树、XZY用DFS将此题在5ms内AC后,本人也跟风用KMP来AC了此题,耗时258ms。我真是太弱了。垃圾地我竟然写了72行!而且还是最慢的。。。

题意:用给定的一些字符串拼凑新的字符串,给定的字符串的可被拼凑的最长前缀的长度是多少?有约200个长度10以内的给定字符串和一个长度200000以内的目标串。字符串可以重复使用。

思路:此题思路较多:

1.XZY:考虑到字符串真正用到的部分较少,匹配时失配几率较大,所以可以使用搜索去逼近并达到最长前缀。

(LINK:)XZY的做法

2.KB:采用TRIE树,应该为本题的正解,但是代码量大,在数据水和常数大的局限下速度也不如搜索快,次解法可以自行百度。

(LINK:)KB的做法

3.我:注意到O(200×200000×k)的复杂度是可行的,所以可以对每一个小字符串对原数组进行KMP匹配,求得每个字符串可以覆盖目标串的部分,再用动态规划求出目标串的最长前缀无重叠覆盖面积。虽然很垃圾,但是AC还是没有问题的。

#include <iostream>
#include <cstdio>
#include <cstring>

#define MX 200001

using namespace std;

int nxt[202][12],lens[201];
char have[MX][201],str[201][11],tar[MX];
int n,lentar,f[MX];

void input()
{
    int len1,len2=0;
    char ch;
    for(n=1;; n++)
    {
        ch=getchar(),len1=0;
        while(ch<'A'||ch>'Z'){if(ch=='.')goto xxxxx;ch=getchar();}
        while(ch>='A'&&ch<='Z')str[n][len1++]=ch,ch=getchar();
    }
    xxxxx:;
    n--;
    while(cin>>ch){if(ch<'A'||ch>'Z')continue;tar[len2++]=ch;}
    lentar=strlen(tar);
    for(int i=1;i<=n;i++)lens[i]=strlen(str[i]);
}

void getnext(char *s,int len,int *dis)
{
    dis[0]=0;
    int i,j=0;
    for(i=1;i<len;i++)
    {
        while(j>0&&s[j]!=s[i])j=dis[j-1];
        if(s[j]==s[i])j++;
        dis[i]=j;
    }
}

void kmp(char *t,char *s,int tlen,int slen,int *nt,int snum)
{
    int i=0,j=0;
    while(i<tlen&&j<slen)
    {
        if(t[i]==s[j])i++,j++;
        else if(j==0)i++;
        else j=nt[j-1];
        if(j==slen)have[i-1][0]++,have[i-1][have[i-1][0]]=lens[snum],j=nt[j-1];
    }
}

void work()
{
    for(int i=1;i<=n;i++)getnext(str[i],strlen(str[i]),nxt[i]);
    for(int i=1;i<=n;i++)kmp(tar,str[i],strlen(tar),strlen(str[i]),nxt[i],i);
    for(int i=0;i<lentar;i++)
        for(int j=1;j<=have[i][0];j++)
            if(i-have[i][j]==-1){f[i]=1;break;}
            else if(f[i-have[i][j]]){f[i]=1;break;}
    for(int i=lentar-1;i>=0;i--)if(f[i]){cout<<i+1<<endl;return;}
    cout<<0<<endl;
}

int main()
{
    input();
    work();
    return 0;
}

分享至ヾ(≧∇≦*)ゝ:
分类: 所有

发表评论

电子邮件地址不会被公开。 必填项已用*标注

你是机器人吗? =。= *