poj 3882(Stammering Aliens) 后缀数组 或者 hash

后缀数组:  构建后缀数组,注意要在字符串莫末尾加上一个没出现过的字符。然后可以2分或者直接扫描,直接扫描需要用单调队列来维护

VIEW CODE

#include<cstdio>
#include<algorithm>
#include<iostream>
#include<cmath>
#include<queue>
#include<stack>
#include<string>
#include<cstring>
#include<map>
#include<vector>
#include<set>
#include<ctime>
#include<stdlib.h>
using namespace std;
const int mmax= 40010;
const int mod=1000000007;
typedef long long LL;
//typedef unsigned long long ULL;
char s[mmax];
int sa[mmax],t[mmax],t2[mmax],c[mmax],n;
void build_sa(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=1;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;
    }
}

int rank[mmax],heigth[mmax];
void getheigth()
{
    for(int i=0;i<n;i++)
        rank[sa[i]]=i;
    int k=0;
    for(int i=0;i<n;i++)
    {
        if(k) k--;
        if(rank[i])
        {
            int j=sa[rank[i]-1];
            while(s[i+k]==s[j+k]) k++;
            heigth[rank[i]]=k;
        }
    }

}

int len[mmax];
int mark[mmax];
int head,end,head1,end1;
int main()
{
    int m;
    while(scanf("%d",&m)&&m)
    {
        head=end=0;
        head1=end1=0;
        scanf("%s",s);
        if(m==1)
        {
            printf("%d %d\n",strlen(s),0);
            continue;
        }
        n=strlen(s);
        s[n++]='$';
        s[n]=0;
        build_sa(131);
        getheigth();
        for(int i=1;i<m;i++)
        {
            while(end>head && heigth[len[end-1]]>= heigth[i])
                end--;
            len[end++]=i;
            while(end1>head1 && sa[mark[end1-1]] <sa[i] )
                end1--;
            mark[end1++]=i;
        }

        int ans=heigth[len[head]];
        int e=sa[mark[head1]];
        for(int i=m;i<n;i++)
        {
            if(i-m+1==len[head])
                head++;
            while(end>head && heigth[len[end-1]]>= heigth[i])
                end--;
            len[end++]=i;

            if(i-m==mark[head1])
                head1++;
            while(end1>head1 && sa[mark[end1-1]] <sa[i] )
                end1--;
            mark[end1++]=i;

            if(heigth[len[head]]>ans)
            {
                ans=heigth[len[head]];
                e=sa[mark[head1]];
            }
            else if(heigth[len[head]]==ans)
                e=max(e,sa[mark[head1]]);
        }
        if(ans==0)
        {
            puts("none");
            continue;
        }
        printf("%d %d\n",ans,e);
    }
    return 0;
}

字符串hash : 直接2分了 很好写的

VIEW CODE

#include<cstdio>
#include<algorithm>
#include<iostream>
#include<cmath>
#include<queue>
#include<stack>
#include<string>
#include<cstring>
#include<map>
#include<vector>
#include<set>
#include<ctime>
#include<stdlib.h>
using namespace std;
const int mmax= 1000010;
const int mod=1000000007;
const int inf=0x3fffffff;
using namespace std;
typedef long long  LL;
typedef unsigned long long ULL;
ULL H[mmax];

map<ULL,int>q;
int m;
char str[mmax];
ULL Pow[mmax];
void build_ha()
{
    int n=strlen(str);
    H[n]=0;
    for(int i=n-1;i>=0;i--)
        H[i]=H[i+1]*131+str[i];
}
ULL Ha[mmax];
bool ok(int k)
{
    int n=strlen(str);
    for(int i=0;i+k-1<n;i++)
    {
        ULL tmp=H[i]-H[i+k]*Pow[k];
        Ha[i]=tmp;
    }
    sort(Ha,Ha+n-k+1);
    int cnt=0;
    for(int i=0;i<n-k+1;i++)
    {
        if( i && Ha[i]==Ha[i-1])
            cnt++;
        else
        {
            if(cnt>=m)
                return 1;
            cnt=1;
        }
    }
    if(cnt>=m)
        return 1;
    return 0;
}
int main()
{
    Pow[0]=1;
    for(int i=1;i<mmax;i++)
        Pow[i]=Pow[i-1]*131;
    while(cin>>m&&m)
    {
        scanf("%s",str);
        int n=strlen(str);
        int l=1,r=n+1;
        build_ha();
        while(l<r)
        {
            int mid=(l+r)>>1;
            if(ok(mid))
                l=mid+1;
            else
                r=mid;
        }

        int ans=r-1;
        if(!ans)
        {
            puts("none");
            continue;
        }
        int e;
        q.clear();
        for(int i=0;i+ans-1<n;i++)
        {
            ULL tmp=H[i]-H[i+ans]*Pow[ans];
            q[tmp]++;
            if(q[tmp]>=m)
                e=i;
        }
        printf("%d %d\n",ans,e);
    }
    return 0;
}
时间: 2024-10-26 21:04:09

poj 3882(Stammering Aliens) 后缀数组 或者 hash的相关文章

POJ 3882 Stammering Aliens 后缀数组height应用

题目来源:POJ 3882 Stammering Aliens 题意:给你m一个一个字符串 求至少出现m次的最长字符串 可以在字符串中重叠出现 思路:二分长度l 然后从height数组中找长度大于等于l的前缀 #include <cstdio> #include <cstring> #include <algorithm> using namespace std; const int maxn = 40010; char s[maxn]; int sa[maxn]; i

UVA 12206 - Stammering Aliens(后缀数组)

UVA 12206 - Stammering Aliens 题目链接 题意:给定一个序列,求出出现次数大于m,长度最长的子串的最大下标 思路:后缀数组,搞出height数组后,利用二分去查找即可 这题之前还写过hash的写法也能过,不过写后缀数组的时候,犯了一个傻逼错误,把none输出成node还一直找不到...这是刷题来第二次碰到这种逗比错误了,还是得注意.. 代码: #include <cstdio> #include <cstring> #include <algori

Poj 3294 Life Forms (后缀数组 + 二分 + Hash)

题目链接: Poj 3294 Life Forms 题目描述: 有n个文本串,问在一半以上的文本串出现过的最长连续子串? 解题思路: 可以把文本串用没有出现过的不同字符连起来,然后求新文本串的height.然后二分答案串的长度K,根据K把新文本串的后缀串分块,统计每块中的原文本串出现的次数,大于原文本串数目的一半就作为答案记录下来,对于输出字典序,height就是排好序的后缀数组,只要按照顺序输出即可. 1 #include <cstdio> 2 #include <cstring>

POJ 3294 Life Forms (后缀数组)

题目大意: 求出在m个串中出现过大于m/2次的子串. 思路分析: 如果你只是直接跑一次后缀数组,然后二分答案扫描的话. 那么就试一下下面这个数据. 2 abcdabcdefgh efgh 这个数据应该输出 efgh 问题就在于对于每一个串,都只能参与一次计数,所以在check的时候加一个标记数组是正解. #include <cstdio> #include <iostream> #include <algorithm> #include <cstring>

POJ 3261 Milk Patterns 后缀数组

用后缀数组求重复出现至少k次的可重叠最长子串的长度, 当然是可以用hash搞的,用后缀数组的话,只要在分组之后看看个数是不是大于等于k #include <cstdio> #include <cstring> #include <algorithm> #include <queue> #include <stack> #include <map> #include <set> #include <climits>

poj 3261 Milk Patterns 后缀数组+二分

1 /*********************************************************** 2 题目: Milk Patterns(poj 3261) 3 链接: http://poj.org/problem?id=3261 4 题意: 给一串数字,求这些数字中公共子串个数大于k的 5 最长串. 6 算法: 后缀数组+二分 7 ***********************************************************/ 8 #incl

POJ 1743 Musical Theme 后缀数组 最长重复不相交子串

Musical ThemeTime Limit: 20 Sec Memory Limit: 256 MB 题目连接 http://poj.org/problem?id=1743 Description A musical melody is represented as a sequence of N (1<=N<=20000)notes that are integers in the range 1..88, each representing a key on the piano. It

poj 2406 Power Strings 后缀数组解法

连续重复子串问题 poj 2406 Power Strings http://poj.org/problem?id=2406 问一个串能否写成a^n次方这种形式. 虽然这题用kmp做比较合适,但是我们还是用后缀数组做一做,巩固后缀数组的能力. 对于一个串,如果能写出a^n这种形式,我们可以暴力枚举循环节长度L,那么后缀suffix(1)和suffix(1 + L)的LCP应该就是 lenstr - L.如果能满足,那就是,不能,就不是. 这题的话da算法还是超时,等我学了DC3再写上来. 其实这

poj 3581 Sequence(后缀数组,离散化)详解

题目链接:http://poj.org/problem?id=3581 题目大意:给一个数列,要求将其分成三段,每段进行翻转后形成后合并成新数列,求按字典顺序最小的新数列. 思路: 注意到题目中数列a0,a2,a3...an-1, a0是最大的,因此将原数列翻转后an-1,an-2,...,a1,a0,求后缀数组, sa[0]所代表的后缀即为所求第一段翻转后的数列,注意到要分成三份,因此sa[0]<2时不可取,此时找sa[1], sa[2]看是否可取.找第一个位置后,设剩下 数列是an-1,an