poj 3693 Maximum repetition substring(后缀数组好题)

来源:互联网 发布:淘宝火拼单 编辑:程序博客网 时间:2024/04/30 20:06
题目:http://poj.org/problem?id=3693
题目大意:就是给你一个字符串,让你找出它的连续的重复次数最多的子串。
思路:罗穗骞论文里的题目,好题!论文地址:http://wenku.baidu.com/view/228caa45b307e87101f696a8.html
先穷举长度L,然后求长度为L的子串最多能连续出现几次。首先连续出现1次是肯定可以的,所以这里只考虑至少2次的情况。假设在原字符串中连续出现2次,记这个子字符串为S,那么S肯定包括了字符r[0],r[L],r[L*2],r[L*3],……中的某相邻的两个。所以只须看字符r[L*i]和r[L*(i+1)]往前和往后各能匹配到多远,记这个总长度为K,那么这里连续出现了K/L+1
次。最后看最大值是多少。穷举长度L的时间是n,每次计算的时间是n/L。所以整个做法的时间复杂度是O(n/1+n/2+n/3+……+n/n)=O(nlogn)。
以上摘自论文。其实我个人感觉这里的关键是怎么样O(1)匹配前面。当我们枚举 i*len 和(i+1)*len 时,有可能这两个点不是
这个重复子串的起点和终点,只是包含。设 p1 = i*len ,p2 = (i+1)*len,先算k = lcp(p1,p2),如果k%len == 0,那么这个
次数肯定就是了,不用管前面,因为前面最多 len-1 个。然后就是 != 0 的情况,怎么搞定前面?设 t = k%len,那么就是说后面多了
t 个匹配的字符,这时前面如果len - t个字符再一样,次数就要 +1,对,这也是唯一的一种次数 +1 的情况,其他情况不可能!所以,
这个时候就只需要算一下lcp(p1-(len-t),p2-(len-t)),比较一下即可。这里还有个地方需要注意:这里只能得出重复的最多次数,
以及这个次数所对应的长度 len,并不能确定位置。所以后面还要再扫描 sa 数组,根据长度和次数,把字典序最小的找出来。
自己做,枚举那里只能想到 +1 这样枚举,这样就是O(n^2)了,想不出这样 O(n*logn)的算法。。 ORZ。。

代码如下:

#include<cstdio>#include<cstring>#include<vector>#include<algorithm>using namespace std;const int MAXN = 111111;char str[MAXN];int sa[MAXN],rank[MAXN],height[MAXN];int t[MAXN],t2[MAXN],c[MAXN];void cal_height(int n){    for(int i = 1;i <= n;i++) rank[sa[i]] = i;    int k = 0;    for(int i = 0;i < n;i++)    {        if(k) k--;        int j = sa[rank[i]-1];        while(str[i+k] == str[j+k]) k++;        height[rank[i]] = k;    }}void da(int n,int m){    int *x = t,*y = t2;    for(int i = 0;i < m;i++) c[i] = 0;    for(int i = 0;i < n;i++) c[x[i] = str[i]]++;    for(int i = 1;i < m;i++) c[i] += c[i-1];    for(int i = n-1;i >= 0;i--) sa[--c[x[i]]] = i;    for(int k = 1;k <= n;k <<= 1)    {        int p = 0;        for(int i = n-k;i < n;i++) y[p++] = i;        for(int i = 0;i < n;i++) if(sa[i] >= k) y[p++] = sa[i] - k;        for(int i = 0;i < m;i++) c[i] = 0;        for(int i = 0;i < n;i++) c[x[y[i]]]++;        for(int i = 1;i < m;i++) c[i] += c[i-1];        for(int i = n-1;i >=0;i--) sa[--c[x[y[i]]]] = y[i];        swap(x,y);        p = 1;        x[sa[0]] = 0;        for(int i = 1;i < n;i++)            x[sa[i]] = y[sa[i]] == y[sa[i-1]] && y[sa[i]+k] == y[sa[i-1]+k] ? p-1 : p++;        if(p >= n) break;        m = p;    }    cal_height(n-1);}int d[MAXN][20];void rmq_init(int n){    for(int i = 1;i <= n;i++)        d[i][0] = height[i];    for(int j = 1;(1<<j) <= n;j++)        for(int i = 1;i+(1<<j) <= n;i++)            d[i][j] = min(d[i][j-1],d[i+(1<<(j-1))][j-1]);}int rmq(int a,int b){    int k = 0;    while((1<<(k+1)) < (b-a+1)) k++;    return min(d[a][k],d[b-(1<<k)+1][k]);}int lcp(int a,int b){    a = rank[a],b =rank[b];    if(a > b) swap(a,b);    return rmq(a+1,b);}int main(){    int cas = 0;    while(~scanf("%s",str))    {        if(str[0] == '#') break;        int n = strlen(str);        printf("Case %d: ",++cas);        da(n+1,128);        rmq_init(n);        int ans_time = 0;        vector <int> ans;        for(int len = 1;len <= n;len++)        {            for(int i = 0;i+len < n;i += len)            {                int j = i+len;                int k = lcp(i,j);                int tmp = k/len+1;                int ii = i-(len-k%len);                if(ii >=0 && k%len != 0)                {                    if(lcp(ii,ii+len) > k)                        tmp++;                }                if(tmp > ans_time)                {                    ans_time = tmp;                    ans.clear();                    ans.push_back(len);                }                else if(tmp == ans_time)                {                    ans.push_back(len);                }            }        }        //printf("ti = %d\n",ans_time);        //for(int i = 0;i < ans.size();i++)            //printf("i = %d,ans = %d\n",i,ans[i]);        int ok = 0;        int ans_pos = 0,ans_len = 1;        for(int i = 1;i <= n && !ok;i++)        {            for(int j = 0;j < ans.size();j++)            {                if(sa[i]+ans[j] >= n) break;                if(lcp(sa[i],sa[i]+ans[j]) >= (ans_time-1)*ans[j])                {                    ans_pos = sa[i];                    ans_len = ans[j]*ans_time;                    ok = 1;                    break;                }            }        }        for(int i = 0;i < ans_len;i++)            printf("%c",str[ans_pos+i]);        puts("");    }    return 0;}/*bacbacb*/


原创粉丝点击