字符串题型练习

🎬 博主名称个人主页

🔥 个人专栏 : 《算法通关》《Java讲解》

⛺️心简单,世界就简单

序言

最近有些摆,emmmm然后水的文章只有最近学校让写的这些练习了,其实这些之前的文章也讲过模板,也确实该练习一下

目录

序言

字符串哈希

KMP

字典树

最长异或和路径

字符串哈希

思路:

题目说让你求得给出的这几个字符串有几个不同的字符串,那我们就可以求出每个字符串的哈希值,然后扔给set,然后直接输出set的size就行

我们求哈希值的过程再来说一下吧

1,**这一步这道题不需要,**先与处理一下,P的几次方,然后我们看一下字符串大小,大约是1000,那我们预处理到1005得到P的1005的大小就行,P取131,然后以前还说要mod 2^64,我们把数据类型给缓存unsigned int就行,这样溢出就相当于mod了

2,计算前i个数的哈希值,也就是每个位置的哈希前缀,这里我用了二维数组存取,也就是h[i][j]意思就是第 i 个字符串的前 j 个字符的哈希前缀,我们字符串的第一位就是换算到二进制算法里的那个乘2的最高位的数,所以每次计算时候h[j - 1] * P

for(int j = 1; j <= sizes; j++){

h[i][j] = h[i][j - 1] * P + str[j];

}

3,这道题很简单了,没有让你计算一个字符串里某个区间的哈希值,那个还要再麻烦一点点

cpp 复制代码
#include<iostream>
#include<string>
#include<string.h>
#include<set>
using namespace std;

typedef unsigned long long ULL;

const int N =1e4 + 10, P = 131;

int n, m;
char str[N];
ULL h[N][1550], p[N];
int t[N];


//用不着
//ULL get(int l, int r){
//	return h[r] - h[l - 1] * p[r - l + 1];
//}

void f(){
	
}

int main(){
	scanf("%d", &n);
	p[0] = 1;
//用不着
	for(int i = 1; i <= 1005; i ++){
		p[i] = p[i - 1] * P;
	}
	
	
	for(int i = 1; i <= n; i++){
		scanf("%s", str + 1);
		int sizes = strlen(str + 1);
	//	cout<<sizes<<'*'; 
	    for(int j = 1; j <= sizes; j++){
		h[i][j] = h[i][j - 1] * P + str[j];
		}
		t[i] = h[i][sizes];
	}
	set<int> st;
	for(int i = 1; i <= n; i++){
		st.insert(t[i]);
	}
	cout << st.size();
}

KMP

思路:这个就是让你计算s2出现在s1所有位置的第一个字符的位置,最后再输出一遍next数组就行

cpp 复制代码
#include<iostream>
#include<string.h>
using namespace std;
 
const int N = 1e6 + 10;
const int M = 1e6 +10;
char S[M], P[N];
int  ne[N]; 
int n, m;
int main(){
	cin >>(S + 1);
	cin >> (P + 1) ;
	n = strlen( P + 1); 
	m = strlen( S + 1);
	//求next过程

	for(int i = 2, j = 0; i <= n;i ++){
		while(j && P[i] != P[j + 1]) j = ne[j];
		if(P[i] == P[j + 1]) j ++; 
		ne[i] = j;
		
	}
	 	
	for(int i = 1, j = 0;i <=m; i++ ){
		while(j && S[i] != P[j + 1]) j = ne[j];
		if(S[i] == P[j + 1]) j ++;
		if(j == n){
			//匹配成功
			printf("%d\n", i - n + 1);//题目字符串从1开始的所以再加个1
		    j = ne[j];//成功后可能后面还有能匹配的,继续操作 
		}
		
	}
	for(int i = 1; i <= n; i ++) cout << ne[i] <<' '; 
	cout<< '\n'; 
	
} 

字典树

思路:这个就是trie树哈,这个就根我之前那个模板题差不多

我再来说说怎么建树,就是想象出来一颗树,然后这个题是字符串含有的是数字和字母,然后我们用数组来存树son[ p ][ u ],p是某个节点,u是这个节点伸出来的字符,然后这个数组就是一个节点idx编号,当我们想向下继续延申,就让p等于这个idx就行,然后继续延申

然后我们之前文章里的模板是只能查出一个字符串是否出现过,这个是查出一个字符串是否在之前的字符串的前缀里,我们的cnt数组就要用来记录节点的出现次数了

cpp 复制代码
#include<iostream>
#include<cstring>
#include<string.h>
using namespace std;
 
const int N = 3e6 + 10;
 
char str[N];
int son[N][65], cnt[N], idx;//下标是0的带你,既是根节点又是 空节点 
//cnt是以当前这个点结尾的单词有多少个
 
//插入操作

int getnum(char x){
    if(x>='A'&&x<='Z')
        return x-'A';
    else if(x>='a'&&x<='z')
        return x-'a'+26;
    else
        return x-'0'+52;
} 

void insert(char str[]){
	int p = 0;//从根节点开始,从0开始遍历 
	for(int i = 0; str[i]; i ++){
		int u =getnum(str[i]);
		//如果p这个节点不存在u这个儿子,就创建一个 
		if(!son[p][u]) son[p][u] = ++idx;
	    
		p = son[p][u]; 
			cnt[p]++;
	}
	
} 
 
//查询操作
int query(char str[]){
	int p = 0;
	int u;
	for(int i = 0; str[i]; i ++){
		u = getnum(str[i]);
		if(!son[p][u]) return 0;
		p = son[p][u];
	}
	return cnt[p];
} 
int main(){
	int t;
	scanf("%d", &t);
	while(t --){
		
		for(int i = 0;i <= idx; i++ ){
			cnt[i] = 0;
	     	for(int j = 0; j<65; j++) 
		      son[i][j] = 0;
		}
		
		 idx = 0; 
	int n, m;
		scanf("%d%d", &n, &m);
		while(n --){
			scanf("%s", str);
	     	insert(str);
		}
		while(m --){
			scanf("%s", str);
			printf("%d\n", query(str)) ;
		}
			
	} 
	
} 

最长异或和路径

思路:这个其实很麻烦的我感觉,就是之前我做的模板计算给一堆数,让找到哪数异或和最大

但是这个还给了路径,但其实仔细观摩后,我们可以发现其实是一回事的,我们先记录所有的异或路径的异或值,然后存起来,然后我们又知道,两个路径相互异或后,相同的那段路径就抵消了,所以我们就可以得到了

1,我们先利用dfs求出所有节点到自己根节点的异或路径值

2,然后就开始正常的insert和query,我们建树是让每个值的二进制给仍在树里

cpp 复制代码
#include<iostream>
#include<algorithm>
#include<string.h>

using namespace std;

const int N = 1e5 + 10, M = 3300000;

int n;
int son[M][2], idx, sum[N];
int a[N];

int h[N * 2], ne[N * 2], w[N * 2], e[N * 2], id;

void add(int a, int b, int c){
	e[id] = b;
	w[id] = c;
	ne[id] = h[a];
	h[a] = id ++;
	
} 

void dfs(int u, int fa){
	for(int i = h[u]; i != -1; i = ne[i]){
		int t = e[i];
		int wei = w[i];
		if(t != fa){
			sum[t] = wei ^ sum[u];
			dfs(t, u);
		}
	}
}

void insert(int x){
	int p = 0;
	for(int i = 31; ~i; i --){
		int &s = son[p][x >> i & 1];
		if(!s) s = ++ idx;
		p = s;
	}
}

int query(int x){
	int res = 0, p = 0;
	for(int i = 31; ~i; i --){
		int s = x >> i & 1;
		if(son[p][!s]){
			res += 1 << i;
			p = son[p][!s];
		} 
		else{
			res += 0 << i;//可以省略
			p = son[p][s]; 
		}
	}
	return res;
}

int main(){
	memset(h, -1, sizeof h);
	cin >> n;
	for(int i = 1; i < n; i ++){
		int u, v, w;
		scanf("%d%d%d", &u, &v, &w);
		add(u, v, w);
		add(v, u, w);
	} 
	
	dfs(1, -1);
	
	int res = 0;
	
	for(int i = 1; i <= n; i ++){
		insert(sum[i]);
	}
	for(int i = 1; i <= n; i ++){
		res = max(res, query(sum[i]));
	}
	cout << res;
	
	
}

码字不易,给个三连吧,谢谢谢谢大家

相关推荐
只是懒得想了5 小时前
C++实现密码破解工具:从MD5暴力破解到现代哈希安全实践
c++·算法·安全·哈希算法
m0_736919105 小时前
模板编译期图算法
开发语言·c++·算法
dyyx1115 小时前
基于C++的操作系统开发
开发语言·c++·算法
m0_736919105 小时前
C++安全编程指南
开发语言·c++·算法
蜡笔小马6 小时前
11.空间索引的艺术:Boost.Geometry R树实战解析
算法·r-tree
-Try hard-6 小时前
数据结构:链表常见的操作方法!!
数据结构·算法·链表·vim
2301_790300966 小时前
C++符号混淆技术
开发语言·c++·算法
我是咸鱼不闲呀6 小时前
力扣Hot100系列16(Java)——[堆]总结()
java·算法·leetcode
嵌入小生0076 小时前
单向链表的常用操作方法---嵌入式入门---Linux
linux·开发语言·数据结构·算法·链表·嵌入式
LabVIEW开发6 小时前
LabVIEW金属圆盘压缩特性仿真
算法·labview·labview知识·labview功能·labview程序