1.字符串哈希
那么字符串Hash,其实就是:构造一个数字使之唯一代表一个字符串。但是为了将映射关系进行一一对应,也就是,一个字符串对应一个数字,那么一个数字也对应一个字符串。使用unsigned long long能够自然溢出(相当于模是pow(2, 64) - 1),也能通过取模的方式。
#include<bits/stdc++.h>
using namespace std;
typedef unsigned long long ull;
const int N = 1e5 + 4;
const ull base = 131;
char s[N], a[N]; //s是被查找串 a是模式串
ull h[N], h1[N], b[N]; //h和h1分别存两个字符串1-i的哈希函数 b数组存b的i次方
ull gethash(ull h[], int l, int r){
return h[r] - h[l - 1] * b[r - l + 1];
}
int main(){
int ans = 0;
cin >> s + 1;
cin >> a + 1;
int slen = strlen(s + 1);
int alen = strlen(a + 1);
b[0] = 1;
for(int i = 1; i <= alen; ++i){
b[i] = b[i - 1] * base;
h[i] = h[i - 1] * base + (int)a[i]; //也可以通过h[i] = h[i - 1] * base + [a[i] - 'a' + 1]等方式,保证后项大于0即可
}
for(int i = 1; i <= slen; ++i){
h1[i] = h1[i - 1] * base + (int)s[i];
}
for(int i = 1; i + alen - 1 <= slen; ++i){
if(gethash(h, 1, alen) == gethash(h1, i, i + alen -1)){
ans++;
}
}
cout << ans;
}
2.Manacher(马拉车)
#include<bits/stdc++.h>
using namespace std;
const int N = 1100010;
char Ma[N * 2]; //构造的字符串,如原字符串aba,构造后为$a#b#a#
int Mp[N * 2]; //加了#后,每个字符串为回文中心的回文半径
char s[N]; //原字符串
void Mancher(char s[], int len){
int l = 0;
Ma[l++] = '$';
Ma[l++] = '#';
for(int i = 0; i < len; ++i){
Ma[l++] = s[i];
Ma[l++] = '#';
}
Ma[l] = 0;
int mx = 0, id = 0;
for(int i = 0; i < l; i ++){
Mp[i] = mx > i?min(Mp[2 * id - 1], mx - i) : 1;
while(Ma[i + Mp[i]] == Ma[i - Mp[i]]) Mp[i]++;
if(i + Mp[i] > mx){
mx = i + Mp[i];
id = i;
}
}
}
int main(){
cin >> s;
int len = strlen(s);
Mancher(s, len);
int ans = 0;
for(int i = 0; i < 2 * len + 2; ++ i){
ans = max(ans, Mp[i] - 1); //Mp[i] - 1表示以每个字符串为回文中心的回文串长度
}
cout << ans << endl;
return 0;
}
3.字典树
字典树也叫Trie树,是一种树形结构,其中每个结点上可以存储(当然也可以不存储),些变量用于表示该字符串的数量(右图中结点上的数字表示结点编号),每条边表示一个字符。假如结点9里面存储一个变量cnt=3
,说明存在3个字符串为“cbc”
每个结点仅存在一条到根节点的路径,这条路径上的所有边就表示一条字符串。
字典树重要函数及代码模板
#include<bits/stdc++.h>
using namespace std;
int nex[N][27]; // nex[i][0]表示从结点i出发,边为a的下一个结点地址
int cnt[N]; // cnt[i]表示以结点i结尾的字符串数量
int idx = 2; // 内存池,用于动态开点,idx=1为根结点
//将字符串插入字典树
void insert(char s[]){
int n = strlen(s + 1);
int x = 1;
for(int i = 1; i <= n; ++i){
if(!nex[x][s[i] - 'a']) nex[x][s[i]-'a'] = idx ++;
x = nex[x][s[i] - 'a'];
}
//如果要记录每个子串出现的次数,可以每经过一个idx,cnt[idx]都+1
cnt[x]++;
}
//查找字符串在字典树出现的次数
int check(char s[]){
int n = strlen(s + 1);
int x = 1;
for(int i = 1; i <= n; ++i){
x = nex[x][s[i] - 'a'];
}
return cnt[x];
}
4.01tire
类比字典树,把数的二进制表示存储在字典树中,树的每个边代表0或1。可以用来解决一些和异或有关的问题。
#include<bits/stdc++.h>
using namespace std;
#define maxn 210000
int a[maxn], ch[maxn][2], val[maxn], n, ans, tot;
void insert(int x)
{
int now = 0;
for (int j = 31; j >= 0; j -- )
{
int pos = ((x >> i) & 1);
if (!ch[now][pos])
ch[now][pos] = ++ tot;
now = ch[now][pos];
}
val[now] = x;
return ;
}
int query(int x)
{
int now = 0;
for (int j = 31; j >= 0; -- j )
{
int pos = ((x >> j) & 1);
if (ch[now][pos ^ 1])
now = ch[now][pos ^ 1];
else
now = ch[now][pos];
}
return val[now];
}
int main()
{
scanf("%d", &n);
for (int i = 1; i <= n; ++ i )
{
cin >> a[i];
insert(a[i]);
}
for (int i = 1; i <= n; ++ i )
{
ans = max(ans, query(a[i]));
}
cout << ans << endl;
return 0;
}
ICPC四川省省赛G. Function
用01tire来存树,每次查询找到异或的最大值和最小值,并依据题意进行相关二分,具体可看本题题意和题解
#include<bits/stdc++.h>
#define F(a, b) fixed << setprecision(b) << a
#define int long long
using namespace std;
const int N = 5e6 + 10;
const int M = 3e5 + 10;
int a[M];
int n, q;
int ch[N][2], ans, tot;
struct node{
int num;
int idx;
} val[N];
void insert(int x,int y){
int now = 0;
for(int j = 30; j >= 0; j--){
int pos = ((x >> j) & 1);
if(!ch[now][pos]) ch[now][pos] = ++tot;
now = ch[now][pos];
}
val[now].num = x;
val[now].idx = y;
return;
}
int querymax(int x){
int now = 0;
for(int j = 30; j >= 0; --j){
int pos = ((x >> j) & 1);
if(ch[now][pos ^ 1])
now = ch[now][pos ^ 1];
else
now = ch[now][pos];
}
return now;
}
int querymin(int x){
int now = 0;
for(int j = 30; j >= 0; --j){
int pos = ((x >> j) & 1);
if(ch[now][pos])
now = ch[now][pos];
else
now = ch[now][pos ^ 1];
}
return now;
}
void solve(int aq, int bq){
int maxidx = val[querymax(aq)].idx;
int maxnum = (a[maxidx] ^ aq) - bq;
int minidx = val[querymin(aq)].idx;
int minnum = (a[minidx] ^ aq) - bq;
if(maxnum * minnum > 0){
cout << "-1" << endl;
return;
}
int l, r, mid;
if(maxidx > minidx){
l = minidx; r = maxidx;
while(l + 1 != r){
mid = l + r >> 1;
if(((a[mid] ^ aq) - bq) * ((a[l] ^ aq) - bq) <= 0){
r = mid;
}
else{
l = mid;
}
}
cout << l << endl;
}
if(maxidx < minidx){
l = maxidx; r = minidx;
while(l + 1 != r){
mid = l + r >> 1;
if(((a[mid] ^ aq) - bq) * ((a[l] ^ aq) - bq) <= 0){
r = mid;
}
else{
l = mid;
}
}
cout << l << endl;
}
}
signed main(){
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
cin >> n >> q;
for(int i = 1; i <= n; ++i){
cin >> a[i];
insert(a[i], i);
}
int aq, bq;
while(q--){
cin >> aq >> bq;
solve(aq, bq);
}
return 0;
}