poj 1226 Substrings(二分 + kmp)

题目:http://poj.org/problem?id=1226
题目大意:给你 n 个字符串,问你他们的最长公共子串的长度,加上不同的一点就是这个最长公共串可以倒序,就是倒序的也要去匹配,然后输出。
思路:基本思想就是先找到长度最小的字符串,然后枚举它的每个子串,判断另外 n - 1个字符串是不是含有,就这样。不过,其中要有优化,首先,其实前面那个找长度最小已经是一个优化了,然后长度用二分,匹配的时候用kmp,然后就OK了!
这道题好像数据不是很强,暴力也能过,匹配的时候不用kmp,直接用 strstr 也可以(虽然我以前不知道有这个函数)。。。 = = 
说到这道题,真是说多了都是泪啊,就因为 get_fail 里那个 s[ j + 1] != s[ i ] 写成 == 了,然后 WA 了半天,自己造的数据竟然都是对的。。 T^T  另外,再提一点,这道题在hdu上也有,那个数据是真心水,怎么错的写的都是AC的,包括我这个 WA 的。。囧

代码如下:

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;

const int MAX_N = 111;

const int MAX_LEN = 111;

char str[MAX_N][MAX_LEN];

int fail[MAX_LEN];

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

char inv[MAX_LEN],pri[MAX_LEN];

int kmp(char *s,int x)
{
    get_fail(s);
    int len1 = strlen(str[x]);
    int len2 = strlen(s);
    int j = -1;
    for(int i = 0;i<len1;i++)
    {
        while(j >= 0 && s[j+1] != str[x][i]) j = fail[j];
        if(s[j+1] == str[x][i]) j++;
        if(j == len2-1) return 1;
    }
    return 0;
}

int main()
{
    int _;
    scanf("%d",&_);
    while(_--)
    {
        int n;
        scanf("%d",&n);
        int min_len = MAX_LEN;
        int min_str;
        for(int i = 0;i<n;i++)
        {
            scanf("%s",str[i]);
            int len = strlen(str[i]);
            if(len < min_len)
            {
                min_len = len;
                min_str = i;
            }
        }

        int ans = 0;
        int l = 1,r = min_len;
        while(l <= r)
        {
            int len = (l + r) >>1;
            int ok = 0;
            for(int i = 0;;i++)
            {
                int j = i + len - 1;
                if(j >= min_len) break;
                int cnt = 0;
                for(int k = i;k<=j;k++)
                    pri[cnt++] = str[min_str][k];
                pri[cnt] = '\0';
                cnt = 0;
                for(int k = j;k>=i;k--)
                    inv[cnt++] = str[min_str][k];
                inv[cnt] = '\0';
                int flag = 1;
                for(int x = 0;x<n;x++)
                    if(x != min_str)
                    {
                        if((!kmp(pri,x)) && (!kmp(inv,x)))
                        {
                            flag = 0;
                            break;
                        }
                    }
                if(flag)
                {
                    ok = 1;
                    break;
                }
            }
            if(ok)
            {
                ans = len;
                l = len + 1;
            }
            else r = len - 1;
        }
        printf("%d\n",ans);
    }
    return 0;
}


因为之前没有用过strstr,用了一下,尝尝鲜,上面的稍微改了一下,也写了一份:

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;

const int MAX_N = 111;

const int MAX_LEN = 111;

char str[MAX_N][MAX_LEN];

int fail[MAX_LEN];

char inv[MAX_LEN],pri[MAX_LEN];

int main()
{
    int _;
    scanf("%d",&_);
    while(_--)
    {
        int n;
        scanf("%d",&n);
        int min_len = MAX_LEN;
        int min_str;
        for(int i = 0;i<n;i++)
        {
            scanf("%s",str[i]);
            int len = strlen(str[i]);
            if(len < min_len)
            {
                min_len = len;
                min_str = i;
            }
        }

        int ans = 0;
        int l = 1,r = min_len;
        while(l <= r)
        {
            int len = (l + r) >>1;
            int ok = 0;
            for(int i = 0;;i++)
            {
                int j = i + len - 1;
                if(j >= min_len) break;
                int cnt = 0;
                for(int k = i;k<=j;k++)
                    pri[cnt++] = str[min_str][k];
                pri[cnt] = '\0';
                cnt = 0;
                for(int k = j;k>=i;k--)
                    inv[cnt++] = str[min_str][k];
                inv[cnt] = '\0';
                int flag = 1;
                for(int x = 0;x<n;x++)
                    if(x != min_str)
                    {
                        if((!strstr(str[x],pri)) && (!strstr(str[x],inv)))
                        {
                            flag = 0;
                            break;
                        }
                    }
                if(flag)
                {
                    ok = 1;
                    break;
                }
            }
            if(ok)
            {
                ans = len;
                l = len + 1;
            }
            else r = len - 1;
        }
        printf("%d\n",ans);
    }
    return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值