hdu5510(并查集+KMP)

/*题解:此题关键在超时,可用并查集来剪枝,把母串的子串都并到母串的集合里
这样的目的是查询的时候直接找当前节点的父亲,如果父亲是该串的子串,则子串
必定也是,将父亲标记为已经访问,不用再多次重复比较子串;如果父亲不是该串
的子串,则直接flag记录该串的下标,该串是满足条件的,最后找出最大下标的该串。
*/
//用kmp算法比较一个串是否是另一个串的子串
#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
char s[502][2002];
const int N=2005;
int Next[N];
int fa[2005],vis[2005];

int Find(int x)
{
    return x==fa[x]?x:fa[x]=Find(fa[x]);
}

void Union(int x,int y)
{
    int f1=Find(x),f2=Find(y);
    fa[f1]=f2;
}

void MakeNext(char t[])
{
	int j,k;
	j=0,k=-1;
	Next[0]=-1;
	int tlen=strlen(t);
	while(j<tlen)
	{
		if(k==-1||t[j]==t[k])
			Next[++j]=++k;
		else k=Next[k];
	}
}

int Kmp_Idx(char s[],char t[])
{
	int i=0,j=0;
	MakeNext(t);
    int slen=strlen(s),tlen=strlen(t);
	while(i<slen&&j<tlen)
	{
		if(j==-1||s[i]==t[j])
		{
			i++;j++;
		}
		else  j=Next[j];
	}
	if(j==tlen)
		return i-tlen;
	else
		return -1;
}

int main()
{
    int t;
    scanf("%d",&t);
    int cas=0;
    while(t--)
    {
       int n;
       scanf("%d",&n);
       for(int i=1;i<=n;i++)
        scanf("%s",s[i]);
       int flag=-1;
       memset(vis,0,sizeof(vis));
       for(int i=0;i<=n;i++)
        fa[i]=i;
       for(int i=2;i<=n;i++)
       {
          memset(vis,0,sizeof(vis));
           for(int j=1;j<i;j++)
           {
               int fj=Find(j);
               //cout<<i<<' '<<j<<' '<<fj<<"&"<<endl;
               if(vis[fj]==1) continue;
               vis[fj]=1;
               int tmp=-1;
                if(strlen(s[i])>=strlen(s[fj]))
                {
                    tmp=Kmp_Idx(s[i],s[fj]);
                    //cout<<i<<' '<<fj<<' '<<tmp<<'*'<<endl;
                    if(tmp!=-1) Union(fj,i);
                }
                if(tmp==-1)
               {
                 flag=max(flag,i);
                // cout<<flag<<'*'<<endl;
                 break;
               }

           }
       }
       printf("Case #%d: %d\n",++cas,flag);
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/zizahn/article/details/52732900