hdu 5510 Bazinga 思路详解 kmp +思维

    经过了计算理论的魔鬼洗礼,感觉脑子都不太好使了!!赶紧找找以前做过的题目提提神!!!好久没看过KMP算法了!来分享一道以前打区域赛的时候碰着的题目吧!(废话有点多- _-')

题目描述:

给定若干个字符串,从前往后依次排列,一次编号为1-N,要求找到一个下标最大的字符串i,这个字符串满足的条件是,在这之前的存在一个字符串不是他的子串。

求解思路:这个题其实跟KMP没多大关系,匹配算法的效率影响不了算法的运行时间,我试过用strstr都能过这道题。问题的关键在于如何减少匹配的次数。要注意到一个这样的规律,假设在分析第K个字符串是否是满足条件的目标串时,先判断第K-1个字符串是第K个字符串的子串,然后再判断第K-2个字符串是不是第K个字符串的子串,但是如果第K-2个字符串是第K-1个字符串的子串,那我们就不用判断第K-2个字符串是否是第K个字符串的子串了!这样就减少了一次匹配次数。

  只要利用一个字符串的子串的子串也是他的子串这样的思想!我们就可以做很多事情了!

   首先预处理,先两两判断前后字符串的关系,判断每个字符串是否是他后一个串的子串,我们就能建立出类似链表一样的归属关系。

  我们从后往前依次分析每个字符串,假设分析到第K个串时,假设第K-1个字符串不是他的子串,可以直接break结束。如果是他的子串,我们可以顺着之前建立的归属链表关系往前找,假设我们往前一直找,找到了第i个字符串,只要没有break,就说明第i+1到K-1的所有串都是第K个串的子串。如果第i个字符串不是第i+1个子串,那我们就对第i个串和第K个串调用一次匹配算法,否则就继续按照链表的规则往前找,不用进行匹配。

    这样至多需要进行K-1 次匹配,出现这样极端的情况的唯一可能就是前K-1个串前后两个串都没有前串属于后串的关系,一旦出现这样的情况,在往前继续分析的时候第K-1 次循环必定会break!分析第K的串时,匹配次数越多,继续往前找的时候匹配次数必定会减少!

  所以匹配的次数至多为(N-1)+次,之前预处理N-1次,最坏情况分析:设最后一个链表断开的地方在K位置,之前最多有K-1个断开的地方,我们需要比较次数为(N-k)*(k-1)。在K = (N+1)/2时取到最大值,所以最坏时间复杂度为O(N*N)。平均时间复杂度为O(N).

#include 
int next[10005];
int T;
char s[505][2005];
bool v[550],flag;
void getnext(char str[],int len)
{
    int k=0;
    next[1]=0;
    for(int i=2;i<=len;i++)
    {
        while(k>0&&str[k+1]!=str[i])
            k=next[k];
        if(str[k+1]==str[i])
            k++;
        next[i]=k;
    }
}

int match(char P[],int lenp,char T[],int lent)
{
    int res=0;
    getnext(P,lenp);
    int k=0;
    for(int i=1;i<=lent;i++)
    {
        while(k>0&&P[k+1]!=T[i])
            k=next[k];
        if(P[k+1]==T[i])
            k++;
        if(k==lenp){
            res++;
            k=next[k];//这儿修改很重要,不然会超时
            return 1;
        }
    }
    return 0;
}
int main()
{
    scanf("%d",&T);
    int n;
    for(int cs = 1;cs<=T;cs++)
    {
        scanf("%d",&n);
        for(int i=1;i<=n;i++)
           scanf("%s",s[i]+1);
        memset(v,true,sizeof(v));
        int sum = 0;
        for(int i=2;i<=n;i++)
        {
            int lent=strlen(s[i]+1);
            int lenp=strlen(s[i-1]+1);
            if(match(s[i-1],lenp,s[i],lent))
                v[i-1]=false;
        }
        printf("Case #%d: ",cs);
        flag = false;
        for(int i=n;i>=1&&!flag;i--)
        {
            for(int j=i-1;j>=1&&!flag;j--)
            {
                if(v[j])
                {
                    int lenw=strlen(s[j]+1);
                    int lent=strlen(s[i]+1);
                    int ans=match(s[j],lenw,s[i],lent);
                    if(ans==0)
                    {
                        printf("%d\n",i);
                        flag = true;
                        break;
                    }
                }

            }
        }
        if(!flag) printf("-1\n");
    }
    //system("pause");
    return 0;
}


你可能感兴趣的:(hdu 5510 Bazinga 思路详解 kmp +思维)