【POJ-1743】后缀数组训练记录

POJ-1743

题目大意:给定一个字符串,求最长的重复的子串,且两个串不能重叠,且长度至少为5

思路:

题目中的重复,为同时加上减去同一个整数,仍算重复

转化成完全重复来判断,即每个字符为这个字符与它下一个字符的差值,这样可以拿后缀数组求解

判断是否有长度为k的子串是相同的,且不重叠,那么求最优,可以利用二分+判定

考虑利用height数组来求解,对于height值求解,对于排序后的Suffix分组,使每组的间的后缀值都不小于k

那么答案一定在同一组中,判断每组的SA最大和最小是否>=k,如果存在,即为有解,否则无解

CODE:

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
int read()
{
    int x=0,f=1; char ch=getchar();
    while (ch<‘0‘ || ch>‘9‘) {if (ch==‘-‘) f=-1; ch=getchar();}
    while (ch>=‘0‘ && ch<=‘9‘) {x=x*10+ch-‘0‘; ch=getchar();}
    return x*f;
}
#define maxn 40010
int wa[maxn],wb[maxn],wv[maxn],ws[maxn];
int S[maxn]; int SA[maxn]; int n;
int cmp(int *r,int a,int b,int l)
{
    return r[a]==r[b]&&r[a+l]==r[b+l];
}
void DA(int *r,int *sa,int n,int m)
{
    int p,*x=wa,*y=wb,*t;
    for(int i=0; i<m; i++) ws[i]=0;
    for(int i=0; i<n; i++) ws[x[i]=r[i]]++;
    for(int i=1; i<m; i++) ws[i]+=ws[i-1];
    for(int i=n-1; i>=0; i--) sa[--ws[x[i]]]=i;
    p=1;for(int j=1;p<n;j*=2,m=p)
    {
        p=0; for(int i=n-j;i<n;i++) y[p++]=i;
        for(int i=0; i<n; i++) if(sa[i]>=j) y[p++]=sa[i]-j;
        for(int i=0; i<n; i++) wv[i]=x[y[i]];
        for(int i=0; i<m; i++) ws[i]=0;
        for(int i=0; i<n; i++) ws[wv[i]]++;
        for(int i=1; i<m; i++) ws[i]+=ws[i-1];
        for(int i=n-1; i>=0; i--) sa[--ws[wv[i]]]=y[i];
        t=x,x=y,y=t,p=1,x[sa[0]]=0;
        for(int i=1; i<n;i++)
            x[sa[i]]=cmp(y,sa[i-1],sa[i],j)?p-1:p++;
    }
}
int rank[maxn],height[maxn];
void calheight(int *r,int *sa,int n)
{
    int i,j,k=0;
    for(i=1;i<=n;i++) rank[sa[i]]=i;
    for(i=0;i<n;height[rank[i++]]=k)
        for(k?k--:0,j=sa[rank[i]-1];r[i+k]==r[j+k];k++);
    return;
}
bool check(int x)
{
    int minn,maxx;
    minn=maxx=SA[1];
    for (int i=2; i<=n; i++)
        {
            if (height[i]>=x && i<n)
                {
                    maxx=max(maxx,SA[i]);
                    minn=min(minn,SA[i]);
                    continue;
                }
            if (maxx-minn>=x) return 1;
            maxx=minn=SA[i];
        }
    return 0;
}
int main()
{
    while (1)
        {
            n=read(); if (!n) break;
            for (int i=0; i<n; i++) S[i]=read();
            for (int i=0; i<n; i++) S[i]=S[i+1]-S[i]+200;
            n--; S[n]=0;
            DA(S,SA,n+1,300);
            calheight(S,SA,n);
            int l=4,r=n,ans=0;
            while (l<=r)
                {
                    int mid=(l+r)>>1;
                    if (check(mid)) ans=mid,l=mid+1;
                    else r=mid-1;
                }
            ans++;
            if (ans<5) puts("0");
                else printf("%d\n",ans);
        }
    return 0;
}

POJ-1743

时间: 2024-08-28 09:40:46

【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 后缀数组 height分组

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

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]后缀数组例题

题目链接:http://poj.org/problem?id=1743 首先,musical theme只与前后位置的增减关系有关,而与绝对的数值无关,因此想到做一次差分. 然后对于差分后的数组,找到最长的出现两次(或两次以上)的一个子串即可.这个如果说两个子串可以交叉的话就好做了,直接取height的最大值即可,但是题目要求不能交叉,前几天一位师兄讲课刚讲了,可以用二分做.对于一个指定长度L,判断它是否可行,就用这个L去划分height数组,对于每个部分的分别看看最远的两个是否没有交叉就可以了

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 1226后缀数组:求出现或反转后出现在每个字符串中的最长子串

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

POJ 3294 后缀数组:求不小于k个字符串中的最长子串

思路:先把所有的串连接成一个串,串写串之前用没出现过的字符隔开,然后求后缀:对height数组分组二分求得最长的公共前缀,公共前缀所在的串一定要是不同的,不然就不是所有串的公共前缀了,然后记下下标和长度即可. 刚开始理解错题意,然后不知道怎么写,然后看别人题解也不知道怎么意思,后面看了好久才知道题目意思理解错了. 时间四千多ms,别人才一百多ms,不知道别人怎么做的-- #include<iostream> #include<cstdio> #include<cstring&