hdu 5510 Bazinga【KMP+暴力剪枝】

Bazinga

Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/65536 K (Java/Others)
Total Submission(s): 2672    Accepted Submission(s): 841

Problem Description

Ladies and gentlemen, please sit up straight.
Don't tilt your head. I'm serious.

For n given strings S1,S2,⋯,Sn, labelled from 1 to n, you should find the largest i (1≤i≤n) such that there exists an integer j (1≤j<i) and Sj is not a substring of Si.

A substring of a string Si is another string that occurs in Si. For example, ``ruiz" is a substring of ``ruizhang", and ``rzhang" is not a substring of ``ruizhang".

Input

The first line contains an integer t (1≤t≤50) which is the number of test cases.
For each test case, the first line is the positive integer n (1≤n≤500) and in the following n lines list are the strings S1,S2,⋯,Sn.
All strings are given in lower-case letters and strings are no longer than 2000 letters.

Output

For each test case, output the largest label you get. If it does not exist, output −1.

Sample Input

4

5

ab

abc

zabc

abcd

zabcd

4

you

lovinyou

aboutlovinyou

allaboutlovinyou

5

de

def

abcd

abcde

abcdef

3

a

ba

ccc

Sample Output

Case #1: 4

Case #2: -1

Case #3: 4

Case #4: 3

Source

2015ACM/ICPC亚洲区沈阳站-重现赛(感谢东北大学)

 

题目大意:

给你n个字符串,让你找一个最大的i,使得在第i个字符串之前的字符串中,至少有一个不是第i个字符串的子串。


思路:


1、首先对应子串与否的判定方面,直接用KMP字符串匹配即可。


2、那么我们应该如何找这个i呢?

①首先我们想到的最简单的方法就是两层for暴力枚举,第一层for确定i,第二层for从第一个字符串开始扫到第i-1个字符串,然后和第i个字符串相匹配,如果没有匹配上,那么就说明i是一个可行解,维护这个最大i即可。

②其上述的时间复杂度为O(n^2*len【len表示字符串长度】),明显是会超时的,那么我们如何进行优化呢?

③想到这样一个问题,如果当前i枚举到了2,而且第一个字符串已经敲定是第二个字符串的子串了,那么当i枚举到3的时候,其实就不用判断第一个字符串是否是第三个字符串的子串的问题了,这个时候我们只需要判断第二个字符串是不是第三个字符串的子串就可以了,因为如果第二个字符串是第三个字符串的子串,那么第一个字符串一定是第三个字符串的子串,如果第二个字符串不是第三个字符串的子串,那么无论第一个字符串是不是第三个字符串的子串,已经无关紧要了,因为这个时候i==3已经是一个可行解了。


3、那么按照上述思路进行算法(0<=i<n):

①初始化l=0,一层for枚举i(i从1开始);

②如果l>=i,跳③,如果l<i,那么判断第l个字符串是不是第i个字符串的子串,如果是,l++,并且继续操作②,否则记录当前i作为答案,并跳③。

③i++;

一直这样操作下去,时间复杂度接近O(n*len);做到了很大的优化。


Ac代码:

#include<string.h>
#include<stdio.h>
using namespace std;
char aa[600][5000];
char a[200005];
char b[2005];
int next[2005];
int lena;
int lenb;
void set_naxt()//子串的next数组
{
    int i=0,j=-1;
    next[0]=-1;
    while(i<lenb)
    {
        if(j==-1||b[i]==b[j])
        {
            i++; j++;
            next[i]=j;
        }
        else
        j=next[j];
    }
}

int kmp()
{
    int i=0,j=0;
    set_naxt();
    while(i<lena)
    {
        if(j==-1||a[i]==b[j])
        {
            i++;j++;
        }
        else
        j=next[j];
        if(j==lenb)
        return 1;
    }
    return 0;
}
int main()
{
    int t;
    int kase=0;
    scanf("%d",&t);
    while(t--)
    {
        int n;
        scanf("%d",&n);
        for(int i=0;i<n;i++)
        {
            scanf("%s",aa[i]);
        }
        int l=0;
        int ans=-1;
        printf("Case #%d: ",++kase);
        for(int i=1;i<n;i++)
        {
            while(l<i)
            {
                strcpy(a,aa[i]);
                strcpy(b,aa[l]);
                lena=strlen(a);
                lenb=strlen(b);
                if(kmp()==1)
                {
                    l++;
                }
                else
                {
                    ans=i+1;
                    break;
                }
            }
        }
        printf("%d\n",ans);
    }
}



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值