[poj 1743]后缀数组例题

题目链接:http://poj.org/problem?id=1743

首先,musical theme只与前后位置的增减关系有关,而与绝对的数值无关,因此想到做一次差分。

然后对于差分后的数组,找到最长的出现两次(或两次以上)的一个子串即可。这个如果说两个子串可以交叉的话就好做了,直接取height的最大值即可,但是题目要求不能交叉,前几天一位师兄讲课刚讲了,可以用二分做。对于一个指定长度L,判断它是否可行,就用这个L去划分height数组,对于每个部分的分别看看最远的两个是否没有交叉就可以了。

#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;

const int maxn=20005;

#define F(x) ((x)/3+((x)%3==1?0:tb))
#define G(x) ((x)<tb?(x)*3+1:((x)-tb)*3+2)
int wa[maxn*3],wb[maxn*3],wv[maxn*3],wss[maxn*3];
int c0(int *r,int a,int b)
{
    return r[a]==r[b] && r[a+1]==r[b+1] && r[a+2]==r[b+2];
}
int c12(int k,int *r,int a,int b)
{
    if (k==2) return r[a]<r[b] || (r[a]==r[b]&&c12(1,r,a+1,b+1));
    else return r[a]<r[b] || (r[a]==r[b]&&wv[a+1]<wv[b+1]);
}
void sort(int *r,int *a,int *b,int n,int m)
{
    int i;
    for (i=0;i<n;i++) wv[i]=r[a[i]];
    for (i=0;i<m;i++) wss[i]=0;
    for (i=0;i<n;i++) wss[wv[i]]++;
    for (i=1;i<m;i++) wss[i]+=wss[i-1];
    for (i=n-1;i>=0;i--) b[--wss[wv[i]]]=a[i];
}
void dc3(int *r,int *sa,int n,int m)
{
    int i,j,*rn=r+n;
    int *san=sa+n,ta=0,tb=(n+1)/3,tbc=0,p;
    r[n]=r[n+1]=0;
    for (i=0;i<n;i++) if (i%3!=0) wa[tbc++]=i;
    sort(r+2,wa,wb,tbc,m);
    sort(r+1,wb,wa,tbc,m);
    sort(r,wa,wb,tbc,m);
    for (p=1,rn[F(wb[0])]=0,i=1;i<tbc;i++)
        rn[F(wb[i])]=c0(r,wb[i-1],wb[i])?p-1:p++;
    if (p<tbc) dc3(rn,san,tbc,p);
    else for (i=0;i<tbc;i++) san[rn[i]]=i;
    for (i=0;i<tbc;i++) if (san[i]<tb) wb[ta++]=san[i]*3;
    if (n%3==1) wb[ta++]=n-1;
    sort(r,wb,wa,ta,m);
    for (i=0;i<tbc;i++) wv[wb[i]=G(san[i])]=i;
    for (i=0,j=0,p=0;i<ta&&j<tbc;p++)
        sa[p]=c12(wb[j]%3,r,wa[i],wb[j])?wa[i++]:wb[j++];
    for (;i<ta;p++) sa[p]=wa[i++];
    for (;j<tbc;p++) sa[p]=wb[j++];
}
void da(int str[],int sa[],int rank[],int height[],int n,int m)
{
    for (int i=n;i<n*3;i++)
        str[i]=0;
    dc3(str,sa,n+1,m);
    int i,j,k=0;
    for (i=0;i<=n;i++) rank[sa[i]]=i;
    for (i=0;i<n;i++)
    {
        if (k) k--;
        j=sa[rank[i]-1];
        while (str[i+k]==str[j+k]) k++;
        height[rank[i]]=k;
    }
}
int a[maxn*3];
int ra[maxn*3],height[maxn*3],sa[maxn*3];
bool xk[maxn];
const int INF=0x3f3f3f3f;

bool check(int k,int n)
{
    for (int i=2;i<=n-1;i++)
        if (height[i]<k) xk[i]=false;
        else xk[i]=true;
    xk[n]=false;
    int left=INF,right=0;
    for (int i=2;i<=n;i++)
    {
        if (!xk[i])
        {
            if (right-left>k) return true;
            left=INF;
            right=0;
        }
        else
        {
            int zb1=sa[i-1];
            int zb2=sa[i];
            if (zb1<left) left=zb1;
            if (zb2<left) left=zb2;
            if (zb1>right) right=zb1;
            if (zb2>right) right=zb2;
        }
    }
    return false;
}

int solve(int n)
{
    // a[0..n-2]
    // ra[0..n-2]
    // sa[1..n-1]
    // height[2..n-1]
    int l=0,r=n-1;
    while (l<r)
    {
        int mid=(l+r+1)/2;
        if (check(mid,n)) l=mid;
        else r=mid-1;
    }
//    return l;
    if (l<4) return 0;
    return l+1;
}

int main()
{
    int n;
    while (~scanf("%d",&n) && n)
    {
        for (int i=0;i<n;i++) scanf("%d",&a[i]);
        for (int i=0;i<n-1;i++) a[i]=a[i+1]-a[i]+88;
//        for (int i=0;i<n-1;i++) printf("%d ",a[i]);
//        printf("\n");
        da(a,sa,ra,height,n-1,177);
        printf("%d\n",solve(n));
    }
    return 0;
}
时间: 2024-08-04 18:14:51

[poj 1743]后缀数组例题的相关文章

POJ 1743 后缀数组:求最长不重叠子串

数据:这题弄了好久,WA了数十发,现在还有个例子没过,可却A了,POJ 的数组也太弱了. 10 1 1 1 1 1 1 1 1 1 1 这组数据如果没有那个n-1<10判断的话,输入的竟然是5,我靠-- 思路:这个题目关键的地方有两个:第一,重复的子串一定可以看作是某两个后缀的公共前缀,第二,把题目转化成去判定对于任意的一个长度k,是否存在长度至少为k的不重叠的重复的子串. 转化成判定问题之后,就可以二分去解答了.在验证判定是否正确时,我们可以把相邻的所有不小于k的height[]看成一组,然后

POJ 1743 (后缀数组+不重叠最长重复子串)

题目链接: http://poj.org/problem?id=1743 题目大意:楼教主の男人八题orz.一篇钢琴谱,每个旋律的值都在1~88以内.琴谱的某段会变调,也就是说某段的数可以加减一个旋律范围的值.问这个谱子内最长不重叠的重复部分大小. 解题思路: 网上题解已经泛滥的题.很多细节都被先辈大神总结了. 在当年后缀数组还不是热门的时候,这题确实是神题. 首先对于旋律变调的处理: 比如123,123,ans=3. 变调之后:456,123,ans=0?不ans=3. 所以不能使用旋律的初始

POJ 1743 后缀数组

题目大意 找到一个连续的子序列连续出现过两次,且这两次不相交,只要子序列中每个数都加/减一个数得到一个新的序列,也可以看作是相同的 那么也就是说这道题目可以转化成找到两个子序列,这两个子序列中每个数前后的差值是相等的 所以我们可以求所有数两两之间的差值,然后根据这个值建立后缀数组后,二分答案来解决问题 1 #include <cstdio> 2 #include <cstring> 3 #include <iostream> 4 5 using namespace st

poj 1743 后缀数组 求最长不重叠重复子串

题意:有N(1 <= N <=20000)个音符的序列来表示一首乐曲,每个音符都是1..88范围内的整数,现在要找一个重复的主题. “主题”是整个音符序列的一个子串,它需要满足如下条件:1.长度至少为5个音符2.在乐曲中重复出现(可能经过转调,“转调”的意思是主题序列中每个音符都被加上或减去了同一个整数值.)3.重复出现的同一主题不能有公共部分. 链接:点我先转化成相邻两项的差值,然后就是找不可重叠重复子串.做法就是二分答案LEN然后根据height值进行分组 第一道后缀数组题,测了一下模板

POJ - 1743 后缀数组 height分组

题意:求最长不可重叠的相同差值子串的长度 这道题算是拖了好几个月,现在花了点时间应该搞懂了不少,尝试分析一下 我们首先来解决一个退化的版本,求最长不可重叠的相同子串(差值为0) 比如\(aabaabaa\), 那么所求的子串有\(aab,aba,baa\)三个 如何求?不妨枚举.枚举是否有长度为\(k\)的最长不可重叠相同子串 可是后缀数组中并不能直接表示出子串,只能间接地用后缀来表示 长度为\(k\)的相同子串\(=>\)最大公共前缀长度为\(k\)的子串\(=>\)最大公共前缀长度大于等于

POJ 2774 后缀数组:求最长公共子串

思路:其实很简单,就是两个字符串连接起来,中间用个特殊字符隔开,然后用后缀数组求最长公共前缀,然后不同在两个串中,并且最长的就是最长公共子串了. 注意的是:用第一个字符串来判断是不是在同一个字符中,刚开始用了第二个字符的长度来判断WA了2发才发现. #include<iostream> #include<cstdio> #include<cstring> #include<algorithm> #include<map> #include<

poj 3261 后缀数组 找重复出现k次的子串(子串可以重叠)

题目:http://poj.org/problem?id=3261 仍然是后缀数组的典型应用----后缀数组+lcp+二分 做的蛮顺的,1A 但是大部分时间是在调试代码,因为模板的全局变量用混了,而自己又忘了,,,等西安邀请赛还有四省赛结束之后,该冷静反思下尝试拜托模板了 错误   :1.k用错,题目的k和模板的k用混; 2.还是二分的C()函数,这个其实跟前一篇<poj 1226 hdu 1238 Substrings 求若干字符串正串及反串的最长公共子串 2002亚洲赛天津预选题>的C函数

poj 3415 后缀数组分组+排序+并查集

Source Code Problem: 3415   User: wangyucheng Memory: 16492K   Time: 704MS Language: C++   Result: Accepted Source Code #include<iostream> #include<cstdio> #include<algorithm> #include<cstring> using namespace std; #define N 510000

POJ 1226后缀数组:求出现或反转后出现在每个字符串中的最长子串

思路:这题是论文里的最后一道练习题了,不过最后一题竟然挺水的. 因为求的是未反转或者反转后,最长公共子串. 刚开始还真不知道怎么构建连接成一个字符串,因为需要有反转嘛! 但是其实挺简单的,把未反转的和反转后的字符串都连起来,中间用未出现过的字符隔开就行了!然后未反转的和反转的在同一组. 二分枚举最长的公共前缀长度,然后统计看看这个最长的长度在不在所有的组里,如果在就符合-- #include<iostream> #include<cstdio> #include<cstrin