JZOJ 4910. 【NOIP2017模拟12.3】子串

本文介绍了一种优化的字符串匹配算法,通过利用子串间的包含关系减少不必要的比较,从而将时间复杂度从O(T⋅N²⋅S)降低到接近O(T⋅N⋅S),并给出了具体的实现代码。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Description

这里写图片描述

Input

这里写图片描述

Sample Input

4
5
ab
abc
zabc
abcd
zabcd
4
you
lovinyou
aboutlovinyou
allaboutlovinyou
5
de
def
abcd
abcde
abcdef
3
a
ba
ccc

Output

这里写图片描述

Sample Output

4
-1
4
3

Data Constraint

这里写图片描述

Solution

  • 我们第一眼可以想到对于每一个串都使用KMP逐一匹配

  • 那么时间复杂度将是 O(TN2S) ,明显超限!

  • 继续思考,知道性质:对于三个串:a,b,c;

  • a是b的子串,b是c的子串;

  • 那么a也会是c的子串!

  • 于是我们想到优化,枚举相邻的两个子串,判断是否是子串

  • 如果是,那么就不需要处理,继续枚举!

  • 如果不是,那么就从下往上对于这个串进行一次逐一比对

  • 再加上是否大于Ans的剪枝优化

  • 时间复杂度可以接近 O(TNS) ,成功AC!

Code

#include<cstdio>
#include<cstring>
using namespace std;
const int N=501,M=2002;
int ans,n;
int a[N],next[N][M];
char s[N][M];
inline int read()
{
    int data=0; char ch=0;
    while(ch<'0' || ch>'9') ch=getchar();
    while(ch>='0' && ch<='9') data=data*10+ch-'0',ch=getchar();
    return data;
}
inline void init()
{
    n=read();
    ans=0;
    for(int i=1;i<=n;i++)
    {
        scanf("%s",s[i]+1); 
        a[i]=strlen(s[i]+1);
        for(int j=2,k=0;j<=a[i];j++)
        {
            while(k && s[i][j]!=s[i][k+1]) k=next[i][k];
            if(s[i][j]==s[i][k+1]) k++;
            next[i][j]=k;
        }
    }
}//KMP的预处理
inline void work()
{
    for(int k=n;k>1 && k>ans;k--)
    {
        bool bz=false;
        for(int i=1,j=0;i<=a[k];i++)
        {
            while(j && s[k][i]!=s[k-1][j+1]) j=next[k-1][j];
            if(s[k][i]==s[k-1][j+1]) j++;
            if(j==a[k-1])
            {
                bz=true;
                break;
            }
        }//两两配对
        if(!bz)
        {
            if(k>ans) ans=k;
            for(int p=n;p>k && p>ans;p--)
            {
                bz=false;
                for(int i=1,j=0;i<=a[p];i++)
                {
                    while(j && s[p][i]!=s[k-1][j+1]) j=next[k-1][j];
                    if(s[p][i]==s[k-1][j+1]) j++;
                    if(j==a[k-1])
                    {
                        bz=true;
                        break;
                    }
                }
                if(!bz)
                {
                    if(p>ans) ans=p;
                    break;
                }
            }//逐一配对
        }
    }
    if(!ans) printf("-1\n"); else printf("%d\n",ans);
}
int main()
{
    int T=read();
    while(T--)
    {
        init();
        work();
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值