大致題意:
給出n個數,把這個數列分為三段,再把三段反轉後連線在一起成為乙個新串,求字典序最小的新串。
大致思路:
由於需要翻轉,所以在輸入時就按照反序輸入。比如樣例輸入是5 10 1 2 3 4。我們從後向前讀入就變為5 4 3 2 1 10。對這列數求出字尾陣列。在大於2的後最中找到最小的字尾並輸出。對於剩下的字首s,我們把s串接到自己後面,也就是ss。再對這個串求出字尾陣列,然後再把s中最小的字首輸出。最後把剩下的串輸出。
對於第二步為什麼要複製剩餘串接在後面,用下面案例說明
10 1 2 2 3 4
第一步翻轉後得到
4 3 2 2 1 10
求出字尾陣列後得到最小的字尾便是:1 10,將其輸出
剩下來的串是 4 3 2 2.
我們如果直接從剩餘串中找到最小字尾的話會產生以下結果。
最小字尾是 2,輸出。
輸出剩餘串 4 3 2。
最後得到1 10 2 4 3 2 很明顯是wrong的
我們把剩餘的串複製到剩餘串的後面。
對 4 3 2 2 4 3 2 2求出字尾陣列。
得到前四個字元的最小的字尾是 2 2,輸出。
輸出剩餘串 4 3.
得到1 10 2 2 4 3
/*
sequence
給定 n個數字組成的序列 a1,a2,..,an。其中 a1比其他數字都大。
現在要把這個序列分成三段,並將每段分別反轉,求能得到的字典序最小的序列是什麼?
要求分得的每段都不為空。
*/ #include#include#include#includeusing namespace std;
const int max_n = 200005;
//輸入
int n,a[max_n],k;
int rank[max_n * 2];
int tmp[max_n * 2];
int rev[max_n * 2],sa[max_n * 2];
//比較(rank[i],rank[i+k])和(rank[j],rank[j+k])
bool compare_sa(int i,int j)
} //計算字串 s的字尾陣列
void construct_sa(int *s,int n,int *sa)
//利用對長度為k的排序的結果對長度為 2k的排序
for(k = 1;k <= n;k *= 2)
for(int i=0;i<=n;i++)
} }
void solve()
//將 p1之後的字串反轉並重複 2次,再計算其後的字尾陣列
int m = n - p1;
reverse_copy(a+p1,a+n,rev);
reverse_copy(a+p1,a+n,rev+m);
construct_sa(rev,m*2,sa);
//確定後兩段的分割位置
int p2;
for(int i=0;i<=2*m;i++)
reverse(a,a+p1);
reverse(a+p1,a+p2);
reverse(a+p2,a+n);
for(int i=0;iprintf("%d\n",a[i]);
} /*
510 1 2 3 4
*/ int main()
solve();
return 0;
}
POJ3581 Sequence(字尾陣列)
題意 給乙個串,串的第乙個字元比後面的都大,要把它分成三段,然後反轉每一段,求能得到的字典序最小的串是什麼。首先,第一段是可以確定的 把原串反轉,因為第乙個字元是最大的,它是唯一的,不存在反轉串的字尾之間有包含關係,所以取最小的字尾這就是第一段的字串 然後後面兩段,如果確定分割位置可以發現這兩段字串...
字尾陣列 poj3581 Sequence
考慮第一次切割,必然切割的是翻轉後字典序最小的字首,偽證 若切割位置更靠前 則會導致第乙個數翻轉後更靠前,字典序必然更大。若切割位置更靠後,則顯然也會導致字典序更大。sa即可 對於第二次切割,有結論 將序列分割成兩段再分別翻轉得到的序列,可以看作是將兩個原序列拼接得到的新序列中的某個字串翻轉得到的序...
POJ3581 Sequence 字尾樹組
題意 n個數字組成的序列,第乙個數字最大,把序列分成3部分,每個部分分別翻轉,輸出翻轉後字典序最小的序列。字尾陣列變一下,先求出 第乙個分割的位置,然後再求一次字尾陣列,求出第二個位置。輸出就好了。此題要採用單組輸入。1 include 2 include 3 include 4 include 5...