字符串專題:POJ3261——字符串哈希

題目描述:

         給出一個數列,求出數列中最長連續子序列,並且滿足該子序列在數列中出現超過k次。

大致思路:

         最開始想的是用字典樹,以數列中每一個元素a[i]爲開頭到末尾的序列都放到Trie中,然後在插入過程中,訪問到一個結點就把結點權值+1,這樣只要在樹中找到權值全部大於K的連續結點個數……但是數據範圍0-1000000,Trie根本應付不了……

於是想後綴數組能不能搞呢?顯然是可以的,先構造出一個後綴數組,然後二分答案就可以了。

不過後綴數組寫起來很複雜,而且數列長度只有20000,所以就想到了用字符串哈希來搞。首先長度L是需要二分搜索的,然後對於每一種L判斷一下長度爲L的字符串出現了至少K次。判斷方法很簡單,枚舉每一個長度L的子串,求出hash值之後,丟到multiset裏邊……每次用一下lower_bound和upper_bound就可以求出該hash值在序列中出現了多少次……但是對於只有20000的數據來說nlogn有點奢侈……n(logn)^2的複雜度就完全沒有問題了。所以計算所有L長度子串哈希值之後,排序一下,之後遍歷一遍數組,求出出現次數的最大值就OK了……

代碼:

這樣看的話代碼量比後綴數組少了不是一星半點……時間也沒差多少。

#include <cstdio>
#include <cstring>
#include <algorithm>
 
using namespace std;
 
typedef unsigned long long ll;
 
const int maxn = 20000 + 10;
const int x = 131;
int m,pos;
ll H[maxn],xp[maxn];
ll hash[maxn];
int rank[maxn];
int s[maxn],n,k;
 
int cmp(const int &a, const int &b){
         returnhash[a] < hash[b] || (hash[a] == hash[b] && a < b);
}
 
int possible(int L) {
         intc = 0;
         pos= -1;
         for(int i = 0; i < n-L+1; i++) {
                   rank[i]= i;
                   hash[i]= H[i] - H[i+L]*xp[L];
         }
         sort(rank,rank+n-L+1, cmp);
         for(int i = 0; i < n-L+1; i++) {
                   if(i == 0 || hash[rank[i]] != hash[rank[i-1]]) c = 0;
                   if(++c >= k) pos = max(pos, rank[i]);
         }
         returnpos >= 0;
}
 
int main() {
         scanf("%d%d",&n,&k);
         for(int i = 0; i < n; i++) {
                   scanf("%d",s+i);
         }
         H[n]= 0;
         for(int i = n-1; i >= 0; i--) H[i] = H[i+1]*x + s[i];
         xp[0]= 1;
         for(int i = 1; i <= n; i++) xp[i] = xp[i-1]*x;
         intL = 1, R = n+1;
         while(R - L > 1) {
                   intM = L + (R-L)/2;
                   if(possible(M)) L = M; else R = M;
         }
         possible(L);
         printf("%d\n",L);
}


發佈了90 篇原創文章 · 獲贊 0 · 訪問量 4萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章