POJ 3581 Sequence ——后缀数组 最小表示法
生活随笔
收集整理的這篇文章主要介紹了
POJ 3581 Sequence ——后缀数组 最小表示法
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
【題目分析】
? ? 一見到題目,就有了一個顯而易見obviously的想法。只需要每次找到倒過來最小的那一個字符串翻轉就可以了。
? ? 然而事情并不是這樣的,比如說505023這樣一個字符串,如果翻轉了成為320505.
? ? 最小的后綴是05,那么得到的字符串是055023,然而最小的結果是全部翻轉,為050523.
? ? 因為我們沒有考慮到翻轉后的字符和未翻轉的字符有可能會連接起來,我們并沒有考慮到連接部分對答案的影響。
? ? 這里我們用最小表示的方法來做這道題目,每次操作完成之后,都需要把翻轉后的串復制兩遍,這樣的話,我們找到在復制的第一遍中的最小的后綴輸出。
? ? 這樣子我們就可以考慮到他本身的翻轉和不翻轉的部分組成的字符串的大小。
? ? 上面的方法就是后綴數組,復制兩次的方法其實就是為了求最小表示的開頭,為O(nlogn)。
? ? 所以我們可以直接用最小表示法來做,而且不需要復制,最小表示法是O(n)的,直接掃一遍就可以得到最小表示的起始位置,然后輸出即可。
? ? 這樣就有了兩種方法。
? ? 然而人比較懶,練練后綴數組好了。
? ? 注意邊界的條件(第一次翻轉最少剩下兩個字符,為后面兩次翻轉留下空間)。
【代碼】
#include <cstdio> #include <cstring> #include <cmath> #include <cstdlib>#include <map> #include <set> #include <queue> #include <string> #include <iostream> #include <algorithm>using namespace std;#define maxn 800005 #define inf 0x3f3f3f3f #define F(i,j,k) for (int i=j;i<=k;++i) #define D(i,j,k) for (int i=j;i>=k;--i)void Finout() {#ifndef ONLINE_JUDGEfreopen("in.txt","r",stdin); // freopen("out.txt","w",stdout);#endif }int Getint() {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; }int n,a[maxn],b[maxn],top;struct SuffixArray{int s[maxn];int tmp[maxn],cnt[maxn],sa[maxn],rk[maxn],h[maxn];void build(int n,int m){int i,j,k;n++;F(i,0,2*n+5) tmp[i]=sa[i]=rk[i]=h[i]=0;F(i,0,m-1) cnt[i]=0;F(i,0,n-1) cnt[rk[i]=s[i]]++;F(i,1,m-1) cnt[i]+=cnt[i-1];F(i,0,n-1) sa[--cnt[rk[i]]]=i; // cout<<" ";F(i,0,n) cout<<b[s[i]]<<" ";cout<<endl;for (k=1;k<=n;k<<=1){F(i,0,n-1){j=sa[i]-k;if (j<0) j+=n;tmp[cnt[rk[j]]++]=j;}sa[tmp[cnt[0]=0]]=j=0;F(i,1,n-1){if (rk[tmp[i]]!=rk[tmp[i-1]]||rk[tmp[i]+k]!=rk[tmp[i-1]+k]) cnt[++j]=i;sa[tmp[i]]=j;}memcpy(rk,sa,n*sizeof(int));memcpy(sa,tmp,n*sizeof(int));if (j>=n-1) break;} // cout<<"SA ";F(i,0,n) cout<<sa[i]<<" ";cout<<endl; // for (j=rk[h[i=k=0]=0];i<n-1;++i,++k) // while (~k&&s[i]!=s[sa[j-1]+k]) h[j]=k--,j=rk[sa[j]+1]; // cout<<"height ";F(i,0,n) cout<<h[i]<<" "; cout<<endl;} }arr;int main() {Finout();n=Getint();F(i,0,n-1) a[i]=Getint(),b[i]=a[i];b[n]=-inf;sort(b,b+n+1);top=unique(b,b+n+1)-b;F(i,0,n-1) a[i]=lower_bound(b,b+top,a[i])-b;F(i,0,n-1) arr.s[n-1-i]=a[i];arr.s[n]=0;arr.build(n-1,2005); // cout<<"build over"<<endl;int tmp=0;while (arr.sa[tmp]<2||arr.sa[tmp]>=n) tmp++;F(i,arr.sa[tmp],n-1) cout<<b[arr.s[i]]<<endl;// cout<<endl;n-=n-arr.sa[tmp];F(i,0,n-1) arr.s[i+n]=arr.s[i];arr.s[2*n]=0;arr.build(2*n-1,2005);tmp=0; while (arr.sa[tmp]<1||arr.sa[tmp]>=n) tmp++;F(i,arr.sa[tmp],n-1) cout<<b[arr.s[i]]<<endl; //cout<<endl;F(i,0,arr.sa[tmp]-1) cout<<b[arr.s[i]]<<endl; //cout<<endl; }
轉載于:https://www.cnblogs.com/SfailSth/p/6344752.html
總結
以上是生活随笔為你收集整理的POJ 3581 Sequence ——后缀数组 最小表示法的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: react-router使用教程
- 下一篇: 值传递和指针传递