51 nod1286三段子串

来源:互联网 发布:2016淘宝助理手机版 编辑:程序博客网 时间:2024/05/17 04:53
1286 三段子串
题目来源: Codility
基准时间限制:1 秒 空间限制:131072 KB 分值: 160 难度:6级算法题
收藏
取消关注
给定一个字符串S,找到另外一个字符串T,T既是S的前缀,也是S的后缀,并且在中间某个地方也出现一次,并且这三次出现不重合。求T最长的长度。
例如:S = "abababababa",其中"aba"既是S的前缀,也是S的后缀,中间还出现了一次,并且同前缀后缀均不重合。所以输出"aba"的长度3。如果找不到一个符合条件的字符,输出0。
Input

输入字符串S。(1 <= L <= 1000000, L为S的长度)

Output

输出这个最长的子串的长度。

Input示例

abababababa

Output示例

3

想法:

非常有意思的一道字符串题目,题面很了然,但这道题难度还是不小的。字符串str的长度高达1 000 000,只能想O(n)的做法了。

    定义suffix(i)为后缀i, 即suffix(i) = str[i]str[i+1]str[i+1]...str[n-1]
    定义d[i]为suffix(i)与str的最长公共前缀

假设已经算出了d[1], d[2], ... , d[i-1](d[0]没必要计算)。定义r为max{d[j] + j - 1 | 1 <= j <= i-1},即曾经匹配的最右边界。同时定义l为取得最右边界r的j。l和r初始化为-1。现在利用这些信息计算d[i]

    当 i <= r
        因为有str[l ... r] == str[0 ... r-l]
        所以str[i ... r] == str[ii ... r-l] (令ii = i - l)
        d[ii]已经求得,下面利用d[ii]快速计算d[i]
            如果i + d[ii] - 1 < r,易知d[i] = d[ii]
            否则,d[i] >= r - i, 可令d[i] = r - i,然后从i + d[i]开始暴力比较即可
    当i > r
        暴力比较

其中,r和l在可更新的时候进行更新即可。

关于时间复杂度 : 从上面可以看出,对于每一次的考虑,要么r增大1,要么比较一次,出现了不匹配,退出。总体上复杂度为O(n)。

代码:

#include <bits/stdc++.h>
using namespace std;
const int N = 1000000 + 10;
int d[N];
char str[N];
void init(int n)
{
    int l = -1, r = -1;
    for(int i = 1; i < n; ++i)
    {
        if(i <= r)
        {
            int ni = i - l;
            if(i + d[ni] - 1 < r) d[i] = d[ni];
            else
            {
                d[i] = r - i;
                while(i + d[i] < n && str[d[i]] == str[i + d[i]])
                {
                    d[i]++;
                    if(i + d[i] - 1 > r)
                    {
                        r = i + d[i] - 1;
                        l = i;
                    }
                }
            }
        }
        else
        {
            d[i] = 0;
            while(d[i] + i < n && str[d[i]] == str[d[i]+i])
            {
                d[i]++;
                if(i + d[i] - 1 > r)
                {
                    r = i + d[i] - 1;
                    l = i;
                }
            }
        }
    }
}

int main()
{
    scanf("%s", str);
    int n = strlen(str);
    init(n);
    int ans = 0, maxx = 0;
    int len = n / 3;
    for(int i = len; i <= n - 2 * len; ++i)
        maxx = max(maxx, d[i]);
    while(len)
    {
        if(d[n-len] == len && maxx >= len)
        {
            ans = len; break;
        }
        len--;
        if(len == 0) break;
        maxx = max(maxx, d[len]);
        maxx = max(maxx, max(d[n-2*len], d[n-2*len-1]));
    }
    printf("%d", ans);
    return 0;
}