哈希(BKDR_hash)+馬拉車


1. 洛谷P3370:字符串哈希模板

Code

 1 #pragma warning (disable:4996)
 2 #include <algorithm>
 3 #include <iostream>
 4 #include <iomanip>
 5 #include <cstring>
 6 #include <string>
 7 #include <cstdio>
 8 #include <queue>
 9 #include <stack>
10 #include <cmath>
11 #include <map>
12 #include <set>
13 using namespace std;
14 typedef long long ll;
15 typedef unsigned long long ull;
16 #define MS(x) memset(x,0,sizeof(x));
17 #define inf 0x3f3f3f3f
18 
19 const int maxn = 1e5 + 5;
20 
21 ull MOD = 19801217;
22 inline ull BKDR_hash(char* str)
23 {
24     unsigned int seed = 131;
25     unsigned int hash = 0;
26     while (*str)
27         hash = hash * seed + (*str++);//這里不能括號MOD(括號優先性,str地址自增運算))
28     return hash;
29 }
30 
31 ull a[maxn];
32 char s[2000];
33 
34 int main()
35 {
36     int n, ans = 1;
37     cin >> n;
38     for (size_t i = 0; i < n; i++)
39     {
40         scanf("%s", s);
41         a[i] = BKDR_hash(s);
42     }
43     sort(a, a + n);
44     for (size_t i = 0; i < n - 1; i++)
45         if (a[i] != a[i + 1])
46             ans++;
47     printf("%d", ans);
48     
49     
50     return 0;
51 }
板子

2. POJ3974/洛谷P3805:哈希+二分->[O(nlogn)](見POJ)、馬拉車算法模板->[O(n)](見洛谷):就是針對你這種最長回文子串

Code

 

 1 #pragma warning (disable:4996)
 2 #include <algorithm>
 3 #include <iostream>
 4 #include <iomanip>
 5 #include <cstring>
 6 #include <string>
 7 #include <cstdio>
 8 #include <queue>
 9 #include <stack>
10 #include <cmath>
11 #include <map>
12 #include <set>
13 using namespace std;
14 typedef long long ll;
15 typedef unsigned long long ull;
16 #define MS(x) memset(x,0,sizeof(x));
17 #define inf 0x3f3f3f3f
18 
19 const int maxn = 1e8 + 15;
20 
21 char transStr[maxn << 1]; //轉換字符串
22 int Len[maxn << 1];    //Trans對應最大回文串長度
23 char rowStr[maxn];   //源字符串
24 
25 int init(char* s)
26 {
27     int len = strlen(s);
28     int l = 0;
29     transStr[l++] = '$';
30     transStr[l++] = '#';
31     for (size_t i = 0; i < len; i++)
32     {
33         transStr[l++] = rowStr[i];
34         transStr[l++] = '#';
35     }
36     transStr[l] = '\0';
37     return l;//轉換字符串的長度
38 }
39 
40 int Manacher(char* s, int len)
41 {
42     int mx = 0, id = 0, ans = 0;//id為已知最大回文子串中心,mx為此子串右邊界
43     for (int i = 0; i < len; i++)
44     {
45         Len[i] = mx > i ? min(Len[2 * id - i], mx - i) : 1;//關於min,若(Len[2*id-1] >= mx - i),則取最小值,之后再匹配更新
46         while (transStr[i + Len[i]] == transStr[i - Len[i]]) Len[i]++;//匹配更新
47         if (i + Len[i] > mx)//若新計算的回文子串右端點大於mx,更新id和mx的值
48         {
49             mx = i + Len[i];
50             id = i;
51         }
52         ans = max(ans, Len[i]);
53     }
54     return (ans - 1); // 什么?為什么減一?你退群吧
55 }
56 
57 int main()
58 {
59 
60     scanf("%s", rowStr);
61     printf("%d\n", Manacher(rowStr, init(rowStr)));
62     return 0;
63 }
manacher

 

3. 洛谷P4503 [CTSC2014]:字符串的哈希預處理枚舉 --(順便記下penguin: 企鵝) QQ: ???

 1 #pragma warning (disable:4996)
 2 #include <algorithm>
 3 #include <iostream>
 4 #include <iomanip>
 5 #include <cstring>
 6 #include <string>
 7 #include <cstdio>
 8 #include <queue>
 9 #include <stack>
10 #include <cmath>
11 #include <map>
12 #include <set>
13 using namespace std;
14 typedef long long ll;
15 typedef unsigned long long ull;
16 #define MS(x) memset(x,0,sizeof(x));
17 #define inf 0x3f3f3f3f
18 
19 const int maxn = 1e8 + 15;
20 
21 ull HashL[30005][205];
22 ull HashR[30005][205];
23 ull temp[30005];
24 char str[205];
25 
26 int N, L, S;
27 ull seed1 = 131;
28 ull seed2 = 13331;
29 void HashInit(int i)
30 {
31     HashL[i][0] = 1;
32     HashR[i][L+1] = 1;
33     for (int j = 1; j <= L; j++)
34         HashL[i][j] = HashL[i][j - 1] * seed1 + str[j];
35     for (int j = L; j >= 1; j--)
36         HashR[i][j] = HashR[i][j + 1] * seed2 + str[j];
37 }
38 
39 int main()
40 {
41     int ans = 0;
42     scanf("%d %d %d", &N, &L, &S);
43     for (size_t i = 1; i <= N; i++)
44     {
45         //也確實你哈希字符串就沒必要把所有字符串存儲起來了,浪費空間
46         scanf("%s", str+1); //程序唯一一次卡了5min是因為這個地方沒有考慮到地址問題,str需要+1(下標影響)(orz氣死)
47         HashInit(i);
48     }
49     for (size_t j = 1; j <= L; j++)
50     {
51         for (int i = 1; i <= N; i++)
52             temp[i] = HashL[i][j - 1] * seed1+ HashR[i][j + 1] * seed2;//這里還要乘以213,233等base值(不乘就WA了嗚嗚。。。
53                                                                     //本着不妥協的精神最后死心眼地改成了seed1,seed然后2AC了:)
54         sort(temp + 1, temp + 1 + N);
55         int now = 1;
56         for (int i = 1; i < N; i++)
57         {
58             if (temp[i] == temp[i + 1]) //2個相似:+1,3個相似:+1+2,4個相似+1+2+3:
59             {
60                 ans += now;
61                 now++;
62             }
63             else
64                 now = 1;
65         }
66     }
67     printf("%d", ans);
68     return 0;
69 }
penguin

4. POJ3359/洛谷SP4354:雪花飄(為什么我之前過了🙈)

於哈希的seed:131,13331等
關於哈希的MOD:19801217,19750817等

Some blogs: https://www.cnblogs.com/Slager-Z/p/7807011.html
👇
hash好像可以暴力水過很多字符串算法。。
1、kmp
問題:給兩個字符串S1,S2,求S2是否是S1的子串,並求S2在S1中出現的次數
把S2 Hash出來,在S1里找所有長度為|S2|

2、AC自動機
問題:給N個單詞串,和一個文章串,求每個單詞串是否是文章串的子串,並求每個單詞在文章中出現的次數。
把每一個單詞hash成整數,再把文章的每一個子串hash成整數,接下來只需要進行整數上的查找即可。
復雜度:O(|A|2+|S|)
用AC自動機可以做到O(|A|+|S|)

3、后綴數組
問題:給兩個字符串S1,S2,求它們的最長公共子串的長度。
將S1的每一個子串都hash成一個整數,將S2的每一個子串都hash成一個整數
兩堆整數,相同的配對,並且找到所表示的字符串長度最大的即可。
復雜度:O(|S1|2+|S2|2)
用后綴數組可以優化到O(|S|log|S|)

4、馬拉車

問題:給一個字符串S,求S的最長回文子串。
先求子串長度位奇數的,再求偶數的。枚舉回文子串的中心位置,然后二分子串的長度,直到找到一個該位置的最長回文子串,不斷維護長度最大值即可。
復雜度:O(|S|log|S|)
用manacher可以做到O(|S|)

5、擴展kmp
問題:給一個字符串S,求S的每個后綴與S的最長公共前綴
枚舉每一個后綴的起始位置,二分長度,求出每個后綴與S的最長公共前綴。
復雜度:O(|S|log|S|)
用extend-kmp可以做到O(|S|)

hash是一種優雅的暴力。因為字符串特殊的性質,我們可以二分地處理它,一般都有單調性。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM