【BZOJ2251】外星聯絡

2251: [2010Beijing Wc]外星聯絡

Time Limit: 30 Sec  Memory Limit: 256 MB
Submit: 870  Solved: 524
[Submit][Status][Discuss]

Description

小 P 在看過電影《超時空接觸》(Contact)以後被深深的打動,決心致力於尋
找外星人的事業。因而,他天天晚上都爬在屋頂上試圖用本身的收音機收聽外星
人發來的信息。雖然他收聽到的僅僅是一些噪聲,可是他仍是按照這些噪聲的高
低電平將接收到的信號改寫爲由 0 和 1 構成的串, 並堅信外星人的信息就隱藏在
其中。他認爲,外星人發來的信息必定會在他接受到的 01 串中重複出現,因此
他但願找到他接受到的 01 串中全部重複出現次數大於 1 的子串。可是他收到的
信號串實在是太長了,因而,他但願你能編一個程序來幫助他。php

Input

輸入文件的第一行是一個整數N ,表明小 P 接收到的信號串的長度。 
輸入文件第二行包含一個長度爲N 的 01 串,表明小 P 接收到的信號串。ios

Output

輸出文件的每一行包含一個出現次數大於1 的子串所出現的次數。輸出的順
序按對應的子串的字典序排列。數組

Sample Input

7
1010101

Sample Output

3
3
2
2
4
3
3
2
2

HINT

 

  對於 100%的數據,知足 0 <=  N     <=3000 

ide

 

Source

/*In Search Of Life*/
#include<cstdio>
#include<cstdlib>
#include<iostream>
#include<cstring>
#include<algorithm>
#include<queue>
#include<iomanip>
#include<stack>
#include<map>
#include<set>
#include<cmath>
#define debug(x) cerr<<#x<<"="<<x<<endl
#define INF 0x7f7f7f7f
#define llINF 0x7fffffffffffll
using namespace std;
typedef pair<int,int> pii;
typedef long long ll;
inline int init()
{
    int now=0,ju=1;char c;bool flag=false;
    while(1)
    {
        c=getchar();
        if(c=='-')ju=-1;
        else if(c>='0'&&c<='9')
        {
            now=now*10+c-'0';
            flag=true;
        }
        else if(flag)return now*ju;
    }
}
inline long long llinit()
{
    long long now=0,ju=1;char c;bool flag=false;
    while(1)
    {
        c=getchar();
        if(c=='-')ju=-1;
        else if(c>='0'&&c<='9')
        {
            now=now*10+c-'0';
            flag=true;
        }
        else if(flag)return now*ju;
    }
}
int n,m,auxa[10005],auxsort[10005],auxb[10005],auxval[10005],sa[10005],height[10005],rank[10005];
char str[10005];
int ans[5000505];
int c[10005];
int cnt=0;
void getsa()
{
    int *x=auxa,*y=auxb,t,cnt=0;m=256;
    for(int i=1;i<=n;i++)auxsort[x[i]=str[i]]++;
    for(int i=2;i<=m;i++)auxsort[i]+=auxsort[i-1];
    for(int i=n;i>=1;i--)sa[auxsort[x[i]]--]=i;
    for(int j=1;cnt<n;j<<=1,m=cnt)
    {
        cnt=0;
        for(int i=n-j+1;i<=n;i++)y[++cnt]=i;
        for(int i=1;i<=n;i++)if(sa[i]-j>0)y[++cnt]=sa[i]-j;
        for(int i=1;i<=n;i++)auxval[i]=x[y[i]];
        for(int i=0;i<=m;i++)auxsort[i]=0;
        for(int i=1;i<=n;i++)++auxsort[auxval[i]];
        for(int i=2;i<=m;i++)auxsort[i]+=auxsort[i-1];
        for(int i=n;i>=1;i--)sa[auxsort[auxval[i]]--]=y[i];
        swap(x,y);cnt=x[sa[1]]=1;
        for(int i=2;i<=n;i++)
        {
            if(y[sa[i]]==y[sa[i-1]]&&y[sa[i]+j]==y[sa[i-1]+j])x[sa[i]]=cnt;
            else x[sa[i]]=++cnt;
        }
    }
    for(int i=1;i<=n;i++)rank[sa[i]]=i;
    cnt=0;
    for(int i=1;i<=n;i++)
    {
        if(rank[i]==1)continue;
        if(cnt)cnt--;
        int j=sa[rank[i]-1];
        while(str[i+cnt]==str[j+cnt])++cnt;
        height[rank[i]]=cnt;
    }
    return;
}
int main()
{
    n=init();scanf("%s",str+1);
    getsa();
    for(int i=n;i>=1;i--)
    {
        for(int j=1;j<=height[i+1];j++)
        {
            c[j]++;
        }
        for(int j=n;j>=height[i+1]+1;j--)
        {
            if(c[j]>1)ans[++cnt]=c[j];
            c[j]=1;
        }
    }
    for(int j=n;j>=1;j--)
    {
        if(c[j]>1)ans[++cnt]=c[j];
    }   
    for(int i=cnt;i>=1;i--)printf("%d\n",ans[i]);
    return 0;
}
View Code

感謝涵哥指導spa

跑了160ms 我以爲仍是挺快的debug

涵哥@goodqt 說這題最快能夠$O(min(字符集大小log字符集大小,n^{2})+n^{2})$code

具體作法是這樣blog

咱們考慮從後往前加入每一個字符串ip

好比說 字符串

5

10101

這樣求出來rank是

01 0

0101 2

1 0

101 1

10101 3

而後咱們從後往前掃,記錄數組ans[]

每次咱們將ans從1到n置爲1

枚舉i從n到1 將1-height[i+1]++ height[i+1]-n掃一下看看有沒有超過1的數 有就所有彈出到一個棧內

而後這樣最後所有彈掉 而後咱們從上往下把全部元素輸出便可

證實:這樣必定知足字典序且不重不漏

每一個子串只會在它應該被統計的時候統計上 且咱們從後往前枚舉 必定保證了字典序大的數最早被壓進去。

證實:這樣必定能統計出答案

每一個重複子串只會在lcp處被計算 那麼顯然最後全部子串都會被統計,得證。

 UPD:

我發現這是我最近作的SA題 寫點東西提醒一下本身

假如遇到bzoj1717這種題 能夠考慮全程用getVal()比較 而不是直接比較是否相等(會出現全等於0的狀況)

以及最大的問題 不要別人說啥就是啥 本身試一試!

相關文章
相關標籤/搜索