字符串算法

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;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值