A - Musical Theme POJ - 1743 (后缀数组+二分)

版权声明:欢迎大佬指正! https://blog.csdn.net/sinat_36215255/article/details/82946750

题目链接https://vjudge.net/problem/POJ-1743

题目大概是给n个数组成的串,求是否有多个“相似”且不重叠的子串的长度大于等于5,两个子串相似当且仅当长度相等且每一位的数字差都相等

本题思路借鉴自:https://www.cnblogs.com/WABoss/p/5199261.html

有个非常巧妙的思路:

  • 首先把问题转化成重复子串的问题:把原串每一位都与前一位相减。这样得出的新串如果有两个长度为n的子串相同,那么它们对应在原串的长度n+1的子串也就相似。

最后大概描述一下不可重叠最长重复子串的解法:

  • O(logn)二分枚举子串长度,判断解是否成立
  • O(n)判断长度是否成立:把互相之间LCP大于等于长度的分为一组,这通过个扫一遍height即可,因为后缀是有序的,相邻的后缀间的LCP必定的极大的;接下来就找到每个组里后缀sa值最大和最小的,如果差值大于(等于)k就成立,因为这样小下标的后缀沿着LCP下去走k步才不会盖到大下标的后缀。

代码如下:

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
#define MAXN 22222
#define INF (1<<30)
int wa[MAXN],wb[MAXN],wv[MAXN],ws[MAXN];
int cmp(int *r,int a,int b,int l)
{
    return r[a]==r[b] && r[a+l]==r[b+l];
}
int sa[MAXN],t[MAXN],t2[MAXN],c[MAXN],n;
int height[MAXN], rankk[MAXN];
void DA(int s[],int m)
{
    int i, *x = t, *y = t2;
    for ( i = 0; i < m; i++)
        c[i] = 0;
    for ( i = 0; i < n; i++)
        c[x[i] = s[i]]++;
    for ( i = 1; i < m; i++)
        c[i] += c[i - 1];
    for (i = n - 1; i >= 0; i--)
        sa[--c[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++)
            c[i] = 0;
        for (i = 0; i < n; i++)
            c[x[y[i]]]++;
        for (i = 0; i < m; i++)
            c[i] += c[i - 1];
        for (i = n - 1; i >= 0; i--)
            sa[--c[x[y[i]]]] = y[i];
        swap(x, y);
        p = 1;
        x[sa[0]] = 0;
        for (i = 1; i < n; i++)
        {
            x[sa[i]] = y[sa[i - 1]] == y[sa[i]] && y[sa[i - 1] + k] == y[sa[i] + k] ? p - 1 : p++;
        }
        if (p >= n)
            break;
        m = p;
    }
}
void getHeight(int s[])
{
    int i, j, k = 0;
    for (i = 0; i < n; i++)
        rankk[sa[i]] = i;
    for (i = 0; i < n; i++)
    {
        if (k)
            k--;
        j = sa[rankk[i] - 1];
        while (s[i + k] == s[j + k])
            k++;
        height[rankk[i]] = k;
    }
}

int a[MAXN],r[MAXN];
bool isok(int k)
{
    bool flag=0;
    int mx=-INF,mm=INF;
    for(int i=2; i<=n; ++i)
    {
        if(height[i]>=k)
        {
            mm=min(mm,min(sa[i],sa[i-1]));
            mx=max(mx,max(sa[i],sa[i-1]));
            if(mx-mm>k)
                return 1;
        }
        else
        {
            mx=-INF,mm=INF;
        }
    }
    return 0;
}
int main()
{
    while(~scanf("%d",&n) && n)
    {
        for(int i=0; i<n; ++i)
            scanf("%d",a+i);
        --n;
        for(int i=0; i<n; ++i)
            r[i]=a[i+1]-a[i]+88;
        r[n]=0;
        DA(r,176);
        getHeight(r);
      //  SA(r,n+1,176);
        int l=0,r=n>>1;
        int ans = 0;
        while(l<=r)
        {
            int mid=(l+r)>>1;
            if(isok(mid))
            {
                ans = mid;
                l=mid+1;
            }

            else
                r=mid-1;
        }
        if(ans>=4)
            printf("%d\n",ans+1);
        else
            printf("%d\n",0);
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/sinat_36215255/article/details/82946750