1. 程式人生 > >SPOJ Repeats(後綴數組+RMQ)

SPOJ Repeats(後綴數組+RMQ)

ani nsis 後綴 contains 枚舉 radi 需要 答案 tar

REPEATS - Repeats

no tags

A string s is called an (k,l)-repeat if s is obtained by concatenating k>=1 times some seed string t with length l>=1. For example, the string

s = abaabaabaaba

is a (4,3)-repeat with t = aba as its seed string. That is, the seed string t is 3 characters long, and the whole string s is obtained by repeating t 4 times.

Write a program for the following task: Your program is given a long string u consisting of characters ‘a’ and/or ‘b’ as input. Your program must find some (k,l)-repeat that occurs as substring within u with k as large as possible. For example, the input string

u = babbabaabaabaaba

b

contains the underlined (4,3)-repeat s starting at position 5. Since u contains no other contiguous substring with more than 4 repeats, your program must output the maximum k.

Input

In the first line of the input contains H- the number of test cases (H <= 20). H test cases follow. First line of each test cases is n - length of the input string (n <= 50000), The next n lines contain the input string, one character (either ‘a’ or ‘b’) per line, in order.

Output

For each test cases, you should write exactly one interger k in a line - the repeat count that is maximized.

Example

Input:
1
17
b
a
b
b
a
b
a
a
b
a
a
b
a
a
b
a
b

Output:
4
since a (4, 3)-repeat is found starting at the 5th character of the input string. Submit solution!

題目鏈接:SPOJ Repeats

論文裏寫的比較模糊,突然就往後匹配了,還往前匹配,完全沒講怎麽匹配啊,代碼還是看這個博客寫的:傳送門

說一下個人理解,為什麽$LCP(i,i+L)/len+1$就是出現的次數?

首先對於一個由循環節構成的字符串$str$,假設它的長度為$len$,最小循環節長度為$k$,那麽對於任意的$0 \le i \le len-1-k$,都有$str[i]==str[k+i]$

現在回到LCP問題上,假設兩個串的公共前綴已知記為$lcp$,我們枚舉的循環節長度為$L$,當前遍歷位置為$i$,那麽顯然有$S[i+j]==S[i+L+j], 0 \le j \le lcp-1$,看這條式子,是不是跟上面的定義式子很像,顯然有$len-1-k=lcp-1$,化簡得$len=lcp+k$,因此僅僅往後推的循環次數是$(lcp+k)/k=lcp/k+1$,那麽僅僅是往後推的最優解,那說不定前面剛好多了幾個位置也是相同前綴,跟$lcp\%L$多出來的數湊一湊又是$L$呢?如果這樣要至少補$lcp-lcp\%L$,因此我們枚舉這個"至少"的位置$i-(lcp-lcp\%L)$,如果這個位置都可以和後面多余的補出一個$L$,那麽往前也肯定是可以的,這裏可能又回想,那幹嘛不再往前考慮考慮,補出2個、3個、4個甚至更多的L呢,應該是沒這個必要,因為假如你前面可以補更多的L,那麽在前幾次遍歷的時候它早就被算進了往後推的$lcp$裏了,不需要多往前考慮,當然全過程要註意下標是否合法,往前推到負數位置肯定是不行的。還有就是這個題一開始的答案一定要是1,因為1是肯定可以的,因此我們是從$L=2$開始枚舉

代碼:

#include <stdio.h>
#include <iostream>
#include <algorithm>
#include <cstdlib>
#include <cstring>
#include <bitset>
#include <string>
#include <stack>
#include <cmath>
#include <queue>
#include <set>
#include <map>
using namespace std;
#define INF 0x3f3f3f3f
#define LC(x) (x<<1)
#define RC(x) ((x<<1)+1)
#define MID(x,y) ((x+y)>>1)
#define fin(name) freopen(name,"r",stdin)
#define fout(name) freopen(name,"w",stdout)
#define CLR(arr,val) memset(arr,val,sizeof(arr))
#define FAST_IO ios::sync_with_stdio(false);cin.tie(0);
typedef pair<int, int> pii;
typedef long long LL;
const double PI = acos(-1.0);
const int N = 50010;
int wa[N], wb[N], cnt[N], sa[N];
int ran[N], height[N];
char s[N];

inline int cmp(int r[], int a, int b, int d)
{
    return r[a] == r[b] && r[a + d] == r[b + d];
}
void DA(int n, int m)
{
    int i;
    int *x = wa, *y = wb;
    for (i = 0; i < m; ++i)
        cnt[i] = 0;
    for (i = 0; i < n; ++i)
        ++cnt[x[i] = s[i]];
    for (i = 1; i < m; ++i)
        cnt[i] += cnt[i - 1];
    for (i = n - 1; i >= 0; --i)
        sa[--cnt[x[i]]] = i;
    for (int k = 1; k <= n; k <<= 1)
    {
        int p = 0;
        for (i = n - k; i < n; ++i)
            y[p++] = i;
        for (i = 0; i < n; ++i)
            if (sa[i] >= k)
                y[p++] = sa[i] - k;
        for (i = 0; i < m; ++i)
            cnt[i] = 0;
        for (i = 0; i < n; ++i)
            ++cnt[x[y[i]]];
        for (i = 1; i < m; ++i)
            cnt[i] += cnt[i - 1];
        for (i = n - 1; i >= 0; --i)
            sa[--cnt[x[y[i]]]] = y[i];
        swap(x, y);
        x[sa[0]] = 0;
        p = 1;
        for (i = 1; i < n; ++i)
            x[sa[i]] = cmp(y, sa[i - 1], sa[i], k) ? p - 1 : p++;
        m = p;
        if (m >= n)
            break;
    }
}
void gethgt(int n)
{
    int i, k = 0;
    for (i = 1; i <= n; ++i)
        ran[sa[i]] = i;
    for (i = 0; i < n; ++i)
    {
        if (k)
            --k;
        int j = sa[ran[i] - 1];
        while (s[j + k] == s[i + k])
            ++k;
        height[ran[i]] = k;
    }
}
namespace SG
{
    int dp[N][17];
    void init(int l, int r)
    {
        int i, j;
        for (i = l; i <= r; ++i)
            dp[i][0] = height[i];
        for (j = 1; l + (1 << j) - 1 <= r; ++j)
        {
            for (i = l; i + (1 << j) - 1 <= r; ++i)
                dp[i][j] = min(dp[i][j - 1], dp[i + (1 << (j - 1))][j - 1]);
        }
    }
    int ask(int l, int r)
    {
        int len = r - l + 1;
        int k = 0;
        while (1 << (k + 1) <= len)
            ++k;
        return min(dp[l][k], dp[r - (1 << k) + 1][k]);
    }
    int LCP(int l, int r, int len)
    {
        l = ran[l], r = ran[r];
        if (l > r)
            swap(l, r);
        if (l == r)
            return len - sa[l];
        return ask(l + 1, r);
    }
}
int main(void)
{
    int T, len, i;
    scanf("%d", &T);
    while (T--)
    {
        scanf("%d", &len);
        for (i = 0; i < len; ++i)
            scanf("%s", s + i);
        DA(len + 1, 130);
        gethgt(len);
        SG::init(1, len);
        int ans = 1;
        for (int L = 1; L < len; ++L)
        {
            for (i = 0; i + L < len; i += L)
            {
                int lcp = SG::LCP(i, i + L, len);
                int cnt = lcp / L + 1;
                int j = i - (L - lcp % L);
                if (j >= 0)
                    cnt = max(cnt, SG::LCP(j , j + L, len) / L + 1);
                ans = max(ans, cnt);
            }
        }
        printf("%d\n", ans);
    }
    return 0;
}

SPOJ Repeats(後綴數組+RMQ)