优势:任意给定一段区间[L,R]可以用O(1)的时间算出来
#include <iostream>
#include <string.h>
using namespace std;
typedef unsigned long long ull;
const int N = 1000010;
const int base = 131;
char str[N];
ull h[N];
int main()
{
scanf("%s",str + 1);
//算每个前缀的哈希值
int n = strlen(str + 1);
for(int i = 1;i <= n;i++)
{
h[i] = h[i - 1] * base + str[i] - 'a' + 1;
}
for(int i = 1;i <= n;i++)
{
printf("%llu\n",h[i]);
}
return 0;
}
//abc
//1
//133
//17426
算任意两段的哈希值的代码:
求某两段是不是一样的,分别求hash值判等
#include <iostream>
#include <string.h>
using namespace std;
typedef unsigned long long ull;
const int N = 1000010;
const int base = 131;
char str[N];
ull h[N],p[N];
ull get(int l,int r)
{
return h[r] - h[l - 1] * p[r - l + 1];
}
int main()
{
scanf("%s",str + 1);
//算每个前缀的哈希值
int n = strlen(str + 1);
p[0] = 1;
for(int i = 1;i <= n;i++)
{
h[i] = h[i - 1] * base + str[i] - 'a' + 1;
p[i] = p[i - 1] * base;
}
int m;
cin>>m;
while(m--)
{
int l,r;
cin>>l>>r;
cout<<get(l,r)<<endl;
}
return 0;
}
/*
abcabcabc
3
1 3
17426
4 6
17426
7 9
17426
*/
兔子与兔子
由于数据比较大,接收时选择scanf,与上面代码就这不一样。【效率快】
#include <iostream>
#include <string.h>
using namespace std;
typedef unsigned long long ull;
const int N = 1000010;
const int base = 131;
char str[N];
ull h[N],p[N];
ull get(int l,int r)
{
return h[r] - h[l - 1] * p[r - l + 1];
}
int main()
{
scanf("%s",str + 1);
//算每个前缀的哈希值
int n = strlen(str + 1);
p[0] = 1;
for(int i = 1;i <= n;i++)
{
h[i] = h[i - 1] * base + str[i] - 'a' + 1;
p[i] = p[i - 1] * base;
}
int m;
cin>>m;
while(m--)
{
int l1,r1,l2,r2;
scanf("%d %d %d %d",&l1,&r1,&l2,&r2);
if(get(l1,r1) == get(l2,r2))
puts("Yes");
else
puts("No");
}
return 0;
}
回文子串的最大长度
上海自来水来自海上
左边的正序 == 右边的逆序
首先,回文串分为两大类:长度是奇数abcba,长度是偶数abba。
①技巧
②枚举中点
③二分半径,一个长度,如果一样就扩大长度,否则缩小长度。
技巧:
abcba --> a#b#c#b#a 奇数个数
abba --> a#b#b#a 也变成奇数个数
s个字母 补上 s-1个字母 变成 2s - 1 (奇数个呗)
#include <iostream>
#include <string.h>
#include <algorithm>
using namespace std;
typedef unsigned long long ULL;
const int N = 2000010, base = 131;
char str[N];
ULL hl[N], hr[N], p[N];
ULL get(ULL h[], int l, int r)
{
return h[r] - h[l - 1] * p[r - l + 1];
}
int main()
{
int T = 1;
while (scanf("%s", str + 1), strcmp(str + 1, "END")) //调用这个函数,判断有没有遇到END,如果有就结束。逗号表达式,用逗号隔开的语句,返回值,只看最后一个语句的返回值 **细节**
{
int n = strlen(str + 1);
for (int i = n * 2; i > 0; i -= 2)
{
str[i] = str[i / 2];
str[i - 1] = 'z' + 1;
}
n *= 2;
p[0] = 1;
for (int i = 1, j = n; i <= n; i ++, j -- )//i,j分别是正序,逆序
{
hl[i] = hl[i - 1] * base + str[i] - 'a' + 1;
hr[i] = hr[i - 1] * base + str[j] - 'a' + 1;
p[i] = p[i - 1] * base;
}
int res = 0;
for (int i = 1; i <= n; i ++ )
{
int l = 0, r = min(i - 1, n - i);
while (l < r)
{
int mid = l + r + 1 >> 1;
if (get(hl, i - mid, i - 1) != get(hr, n - (i + mid) + 1, n - (i + 1) + 1)) r = mid - 1;//注意端点值的确定,自己举俩例子判断找的对不对
else l = mid;
}
if (str[i - l] <= 'z') res = max(res, l + 1);
else res = max(res, l);
}
printf("Case %d: %d\n", T ++, res);
}
return 0;
}
后缀数组
/*
ponoiiipoi
10 i
9 oi
8 poi
7 ipoi
6 iipoi
5 iiipoi
4 oiiipoi
3 noiiipoi
2 onoiiipoi
1 ponoiiipoi
输出字典序最小的……
输出排名为 i 的后缀与排名为 i-1 的后缀,把二者的最长公共前缀的长度记为 Height[i]
*/
#include <iostream>
#include <algorithm>
#include <string.h>
#include <limits.h>
using namespace std;
typedef unsigned long long ULL;
const int N = 300010, base = 131;
int n;
char str[N];
ULL h[N], p[N];
int sa[N];
int get(int l, int r)
{
return h[r] - h[l - 1] * p[r - l + 1];
}
int get_max_common_prefix(int a, int b)
{
int l = 0, r = min(n - a + 1, n - b + 1);
while (l < r)
{
int mid = l + r + 1 >> 1;
if (get(a, a + mid - 1) != get(b, b + mid - 1)) r = mid - 1;
else l = mid;
}
return l;
}
bool cmp(int a, int b)
{
int l = get_max_common_prefix(a, b);
int av = a + l > n ? INT_MIN : str[a + l];
int bv = b + l > n ? INT_MIN : str[b + l];
return av < bv;
}
int main()
{
scanf("%s", str + 1);
n = strlen(str + 1);
p[0] = 1;
for (int i = 1; i <= n; i ++ )
{
h[i] = h[i - 1] * base + str[i] - 'a' + 1;
p[i] = p[i - 1] * base;
sa[i] = i;
}
sort(sa + 1, sa + 1 + n, cmp);
for (int i = 1; i <= n; i ++ ) printf("%d ", sa[i] - 1);
puts("");
for (int i = 1; i <= n; i ++ )
if (i == 1) printf("0 ");
else printf("%d ", get_max_common_prefix(sa[i - 1], sa[i]));
puts("");
return 0;
}