[POJ2774][后缀数组求LCP]Long Long Message

来源:互联网 发布:mysql unique 编辑:程序博客网 时间:2024/06/09 16:18
[Problem Description]
The little cat is majoring in physics in the capital of Byterland. A piece of sad news comes to him these days: his mother is getting ill. Being worried about spending so much on railway tickets (Byterland is such a big country, and he has to spend 16 shours on train to his hometown), he decided only to send SMS with his mother.

The little cat lives in an unrich family, so he frequently comes to the mobile service center, to check how much money he has spent on SMS. Yesterday, the computer of service center was broken, and printed two very long messages. The brilliant little cat soon found out:

1. All characters in messages are lowercase Latin letters, without punctuations and spaces.
2. All SMS has been appended to each other – (i+1)-th SMS comes directly after the i-th one – that is why those two messages are quite long.
3. His own SMS has been appended together, but possibly a great many redundancy characters appear leftwards and rightwards due to the broken computer.
E.g: if his SMS is “motheriloveyou”, either long message printed by that machine, would possibly be one of “hahamotheriloveyou”, “motheriloveyoureally”, “motheriloveyouornot”, “bbbmotheriloveyouaaa”, etc.
4. For these broken issues, the little cat has printed his original text twice (so there appears two very long messages). Even though the original text remains the same in two printed messages, the redundancy characters on both sides would be possibly different.

You are given those two very long messages, and you have to output the length of the longest possible original text written by the little cat.

Background:
The SMS in Byterland mobile service are charging in dollars-per-byte. That is why the little cat is worrying about how long could the longest original text be.

Why ask you to write a program? There are four resions:
1. The little cat is so busy these days with physics lessons;
2. The little cat wants to keep what he said to his mother seceret;
3. POJ is such a great Online Judge;
4. The little cat wants to earn some money from POJ, and try to persuade his mother to see the doctor :(
[Algorithm]
后缀数组 LCP
[Analysis]
题目要求最长公共子序列,n比较大,普通的n^2肯定是过不了的。这里利用的是后缀数组求最长公共前缀,后缀数组的效率是O(nlogn),求height数组的效率是O(n),就可以完美的解决问题。将两个字符串合并,中间加入一个哨兵元素(注意哨兵必须要小于其他字符,因为要保证短的在后缀数组里面排名靠前)。求后缀数组,height数组。height数组中最大且它所对应的两个后缀在不同的字符串中的值就是答案。(因为其它后缀之间的LCP为sa中它们之间所有height的最小值,所以最大的值一定是在挨着的两个元素之间产生的)。
[Pay Attention]
哨兵元素一定要取比其它所有字符都小!
[Code]

#include <cstdio>

#include <cstring>

#include <cstdlib>

#include <cmath>

#include <iostream>

using namespace std;

const int MAXLEN = 200100;

const int MAXLETTER = 27;

char s1[MAXLEN], s2[MAXLEN], s[MAXLEN];

int len1, len2, len;

int m;

int height[MAXLEN], cc[MAXLEN], t1[MAXLEN], t2[MAXLEN];

int Rank[MAXLEN], sa[MAXLEN];

inline void MakeSA()

{
    int *x = t1, *y = t2;
    memset(cc, 0, sizeof(cc));
    memset(x, 0, sizeof(x));
    int m = MAXLETTER;
    for (int i = 0; i < len; i++) ++cc[x[i] = s[i] - 'a' + 1];
    for (int i = 1; i < m; i++) cc[i] += cc[i - 1];
    for (int i = len - 1; i >= 0; i--) sa[--cc[x[i]]] = i;

    for (int k = 1; k < len; k <<= 1)
    {
        int p = 0;
        for (int i = len - k; i < len; i++) y[p++] = i;
        for (int i = 0; i < len; i++)
            if (sa[i] >= k) y[p++] = sa[i] - k;
        memset(cc, 0, sizeof(cc));
        for (int i = 0; i < len; i++) ++cc[x[y[i]]];
        for (int i = 1; i < m; i++) cc[i] += cc[i - 1];
        for (int i = len - 1; i >= 0; i--) sa[--cc[x[y[i]]]] = y[i];
       
        int *temp = x; x = y; y = temp;
        p = 1; x[sa[0]] = 0;
        for (int i = 1; i < len; i++)
            x[sa[i]] = y[sa[i]] == y[sa[i - 1]] && y[sa[i] + k] == y[sa[i - 1] + k] ? p - 1 : p++;
        m = p;
    }
}
inline void MakeLCP()

{
    for (int i = 0; i < len; i++) Rank[sa[i]] = i;
    height[0] = 0;
    int k = 0;
    for (int i = 0; i < len; i++)
    {
        if (!Rank[i]) continue;
        int j = sa[Rank[i] - 1];
        if (k) k--;
        while (s[i + k] == s[j + k]) k++;
        height[Rank[i]] = k;
    }

}

int main()

{
    //freopen("input.txt", "r", stdin);
    while (scanf("%s", s1) != EOF)
    {
        scanf("%s", s2);
        len1 = strlen(s1), len2 = strlen(s2);
        strcpy(s, s1);
        s[len1] = 'a' - 1;
        s[len1 + 1] = '\0';
        strcat(s, s2);
        len = strlen(s);
        MakeSA();
        MakeLCP();
        int ans = 0;
        for (int i = 1; i < len; i++)
        if (sa[i] > len1 && sa[i - 1] < len1 || sa[i] < len1 && sa[i - 1] > len1)
            ans = ans < height[i] ? height[i] : ans;
        printf("%d\n", ans);
    }

}
0 0