哈希(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是一种优雅的暴力。因为字符串特殊的性质,我们可以二分地处理它,一般都有单调性。

 

posted @ 2019-08-16 08:35  吴下阿萌  阅读(857)  评论(0编辑  收藏  举报