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