試題 演算法提高 著急的wyf(不同子串個數)
提交此題
資源限制
時間限制:476ms 記憶體限制:256.0mb
問題描述
由於戰網的密碼是一串亂碼,wyf巧妙地忘記了他的密碼。(他就是作死,如同自掘墳墓。說到掘墳墓,問題就來了——挖掘機技術究竟哪家強?)他現在非常著急,走投無路,都快飛起來了。他只記得他的密碼是某個字串s的子串。現在問題來了,你要告訴他有多少種可能的密碼,以幫助他確定他能在多少時間內完成列舉並嘗試密碼的工作。
輸入格式
輸入僅包含一行,為字串s,不含空格。
輸出格式
輸出乙個整數,表示可能的密碼數量。
樣例輸入
total
樣例輸出
14資料規模和約定
對於70%的資料,s的長度不超過1000;(暴力)
對於100%的資料,s的長度不超過15000。(suffix array)
推薦擁有字尾陣列和桶排法技能的人進行**!!開個玩笑,本題解法使用了字尾陣列的改裝,所以需要對字尾陣列有一定的了解。講字尾陣列的網上有很多,而且我覺得已經寫得很好了,這裡就不過多的進行闡述了。
這題目乍一看好難下手,暴力求解妥妥的超時。那我們把這個題目,大題化小,小題化了…
依次計算出,長度為1,2,3,…的子串不重複數。比如:計算長度為4的子串數,此時已知長度為3的子串rank排名,把長度3子串左邊乙個字元當『十位』,長度3子串當『個位』,桶排一下。最後比較一下相鄰排名是不是重複的就行了。
ps.看懂了這段的就直接跳到**吧(表達的不是很好,估計都看不懂吧)。
首先,把不同長度的子串拆開了算,最後加一起就行了。這樣,我們考慮的就是一堆長度固定的字串,求不重複的有幾個。
嗯,還是挺麻煩的,開個掛先:假如我把第一步的字串從大到小標記好了(按字典序標記,最小的標記為1,重複的標記數字相同),但是我又在每個子串的前面加了乙個隨機的字元,問:現在有幾個不重複的字串。
這就簡單了,桶排一下就好了,新加的作為左邊,原來的作為右邊(已排序)。用左邊的字元劃分檔次,原順序不變的填入響應的檔次裡,排好了,重複的合併一下就ok了。
事實上,我們的假設:把第一步的字串從大到小標記好了。是可以成立的,因為一開始是計算單個字元的rank排名。
舉個例子:
字串:aabab
長度為1子串有5個:a,a,b,a,b 給個預設排名:1,2,3,4,5進行一波桶排,第乙個字元為』a』的為第一檔次,排名序號為:1-3 『b』:4-5 同一檔次,根據預設排名的先後,區分先後。
初步排名更新為:1,2,4,3,5(這個排名留著等下還有用)
然後進行精確排名:從第2名開始,比較自己與前一名子串的第乙個字元和剩餘字元是否都相同(這裡只有1個字元,就只比較這1個字元),若都相同則並列名次(廢話,大家都是100分,當然並列第一名嘍)。2與1比較,大家都是』a』;3與2比較,大家都是』a』;5與4比較,大家都是』b』。所以精確排名更新為:1,1,2,1,2
叮!總子串數+2
然後是長度為2:a-a,a-b,b-a,a-b 很明顯,取長度1子串後面4個,前面加上1個字元。它們通過第一步是有初始排名的(同理 第一步的預設排名),這裡取第一步的初步排名:2,4,3,5 **斷了,就後面的都前進1 -->1,3,2,4進行一波桶排,'a』檔次有三個,佔掉前三:1-3 『b』:4 同一檔次,根據初始排名的先後,區分先後。
初步排名更新為:1,2,4,3
精確排名:(比較相鄰排名,左部和右部是否都相同)1,2,3,2
叮!總子串+3
長度為3:a-ab,a-ba,b-ab初始排名:2,4,3->1,3,2
初步排名:1,2,3
精確排名:1,2,3
叮!總子串+3
叮!!未發現並列,執行清算(如果某長度不存在重複,那麼在前面加字元還是不重複的)總子串+2+1.
再來乙個例子:ababac
長度1:
預設排序:1,2,3,4,5,6
初步排序:1,4,2,5,3,6
精確排序:1,2,1,2,1,3 叮+3
長度2:
初始排序:4,2,5,3,6->3,1,4,2,5 取上一步初步排序的後n-1個
初步排序:1,4,2,5,3
精確排序:1,3,1,3,2 叮+3
長度3:
初始排序:4,2,5,3->3,1,4,2
初步排序:1,3,2,4
精確排序:1,2,1,3 叮+3
長度4:
初始排序:3,2,4->2,1,3
初步排序:1,3,2
精確排序:1,3,2 叮+3
叮叮+2+1(長度為5,6的不用算了,肯定沒有重複的)
**:
//求不重複子串數
#include
using
namespace std;
const
int max =
1e6+5;
char s[max]
;int m, n;
//m是種類數量,n是字串長度
long
long sum =0;
//不重複子串數
int rak[max]
, tp[max]
, sa[max]
, tax[max]
;//rak i是位置 rak[i]表示第i個位置的排名是多少
//sa i是排名 sa[i]表示第i名的位置是多少
voi***(
int tt,
int nn = n)
//輸出陣列,除錯的時候用的
cout << endl;
}void
finallyadd
(int x)
//當一種長度的子串不存在重複的,那麼它後面的也肯定不會重複
void
sort2
(int a,
int b)
//a未使用
bool
comp
(int r,
int a,
int b,
int k =1)
//若左部和右部都相同,精確排名不增加
return r[a + k]
== r[b + k]
&& s[a]
== s[b]
;//r + s可以判斷是否相同。
}void
get_sa
(int a,
int b)
}}intmain()
執行結果截圖:
求最長重複子串 和 最長不重複子串 思路
題目 求任意乙個字串中的所有最長重複字串和所有最長不重複子串 最長不重複子串的解法 設定乙個輔助資料結構 如map 記錄每個字元最後一次出現的位置 遍歷字串中的每個字元,如果在map中沒有出現,則不重複子串的長度 1,並更新最大字串的長度值 如果在map中已經出現過,則更新當前字元在map中的位置和...
最長不重複子串
出處 勇幸 thinking 題 從乙個字串中找到乙個連續子串,該子串中任何兩個字元不能相同,求子串的最大長度並輸出一條最長不重複子串。本節從最直接的方法逐步優化,漸進探索了四種實現方式,並最終找到時間複雜度為o n 輔助空間為常數的方案,內容如下 基本演算法 使用hash dp方案 dp hash...
最長不重複子串
演算法參考 找到乙個字串中的乙個連續子串,這個子串內不能有任何兩個字元是相同的,並且這個子串是符合要求的最長的。例如輸入 abcbef 輸出 cbef o n 的演算法,具體思路如下 以abcbef這個串為例,用乙個陣列pos記錄每個元素曾出現的下標,初始化為 1。從s 0 開始,依次考察每個字元,...