kmp算法练习 poj 1226 poj 1961

6 篇文章 0 订阅

poj 1226

题目大意:给出一组字符串,要你求出这些字符串的最长公共子串的长度,公共子串可以正序或逆序匹配

解题思路:kmp枚举,枚举第一个字符串的每个子串的正序和逆序,与其余的字符串匹配看是否是其子串,求出最大的子串长度。

这题也可以用后缀树求,留待后面研究。。。。

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

const int maxn = 101;
char str[maxn][maxn];
int next[maxn], rnext[maxn], t, n;

void get_next(int start, int end, bool flag);
bool kmp(int index, int start, int end, bool flag);

int main()
{
    scanf("%d", &t);
    while(t-- != 0)
    {
        scanf("%d", &n);
        for(int i = 0; i < n; i++)
            scanf("%s", str[i]);
        int len = strlen(str[0]), ans = 0;
        for(int i = 0; i < len; i++)
        {
            int l;
            for(int j = i; j < len; j++)
            {
                l = j - i + 1;
                get_next(i, j, true);
                get_next(i, j, false);
                int cnt = 1;
                for(int k = 1; k < n; k++)
                {
                    if(kmp(k, i, j, true) || kmp(k, i, j, false))
                        cnt++;
                    else
                        break;
                }
                if(cnt == n && ans < l)
                    ans = l;
            }
        }
        printf("%d\n", ans);
    }
    return 0;
}

void get_next(int start, int end, bool flag)
{
    int i, j;
    if(flag)
    {
        next[start] = start - 1;
        i = start; j = start - 1;
        while(i <= end)
        {
            if(j == start - 1 || str[0][i] == str[0][j])
            {
                i++;
                j++;
                next[i] = j;
            }
            else
                j = next[j];
        }
    }
    else
    {
        rnext[end] = end + 1;
        i = end; j = end + 1;
        while(i >= start)
        {
            if(j == end + 1 || str[0][i] == str[0][j])
            {
                i--;
                j--;
                rnext[i] = j;
            }
            else
                j = rnext[j];
        }
    }
}

bool kmp(int index, int start, int end, bool flag)
{
    int i, j, k, len;
    len = strlen(str[index]);
    if(flag)
    {
        for(i = 0, j = start; i < len && j <= end;)
        {
            if(j == start - 1 || str[index][i] == str[0][j])
                i++, j++;
            else
                j = next[j];
        }
        if(j > end)
            return true;
    }
    else
    {
        for(i = 0, j = end; i < len && j >= start;)
        {
            if(j == end + 1 || str[index][i] == str[0][j])
                i++, j--;
            else
                j = rnext[j];
        }
        if(j < start)
            return true;
    }
    return false;
}

poj 1961

题目大意,给出一个字符串,若1~~i长的子字符串,是由几个相同的字符串挂接拼凑而成,从小到大输出i和重复字符串的个数

解题思路,kmp,利用kmp中next数组的特性,当i字符不匹配时,移到next[i]位置做比较,所以同一字符串里1~~next[i] - 1 和 i - (next[i] - 1) - 1 ~~~i字符串是匹配的,求出1~i是否由某个字符串连续拼接而成,只要知道i是否能整除next[i]~~~i的长度

如上图,第一副图,红色区域代表字符串是匹配的,那么next[i]~~~i为中间的白色加后面的红色区域,肯定不能被i整除

第二幅图是字符串第一个白色区域加绿色区域和 绿色区域加第二个白色区域的字符串匹配,根据next数组的特性1~~next[i] - 1 和 i - (next[i] - 1) - 1 ~~~i字符串是匹配的,那么第一块绿色区域的字符串和前面的白色匹配即1~~k 和 i - (next[i] - 1) - 1 ~  i - (next[i] - 1) - 1 + k -1 匹配。如此推导下去,每块区域都是相互匹配的即next[i]~i(最后的白色区域)能被i整除

另外一种情况时1~~k和k+1~~i匹配,显然能被i整除

所以判断的条件就是是否next[i]~i能被i整除

#include <iostream>
#include <cstdio>
#include <cstring>

using namespace std;

const int maxn = 1000010;

char str[maxn];
int next[maxn], n;

void get_next();

int main()
{
    int test = 1;
    while(true)
    {
        scanf("%d", &n);
        if(n == 0)
            break;
        printf("Test case #%d\n", test);
        scanf("%s", str);
        get_next();
        for(int i = 1; i < n; i++)
        {
            int k, len;
            len = i + 1;
            if(len % (len - next[len]) == 0)
            {
                if( (k = len / (len - next[len])) > 1)
                    printf("%d %d\n", i + 1, k);
            }
        }
        printf("\n");
        test++;
    }
    return 0;
}

void get_next()
{
    int i = 0, j = -1;
    next[0] = -1;
    while(i < n)
    {
        if(j == -1 || str[i] == str[j])
        {
            i++; j++;
            next[i] = j;
        }
        else
            j = next[j];
    }
}


 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值