KMP
#include<bits/stdc++.h>
using namespace std;
const int N=100010;
int ne[N];
int main(){
int len1,len2;
string s1,s2;
cin>>len1>>s1>>len2>>s2;
s1=' '+s1;
s2=' '+s2;
for(int i=2,j=0;i<=len1;i++){
while(j&&s1[j+1]!=s1[i])j=ne[j];
if(s1[j+1]==s1[i])j++;
ne[i]=j;
}
for(int i=1,j=0;i<=len2;i++){
while(j&&s1[j+1]!=s2[i])j=ne[j];
if(s1[j+1]==s2[i])j++;
if(j==len1){
cout<<i-len1<<' ';
}
}
return 0;
}
Trie - 字符串统计
维护一个字符串集合,支持两种操作:
I x
向集合中插入一个字符串 $x$;Q x
询问一个字符串在集合中出现了多少次。 共有 $N$ 个操作,所有输入的字符串总长度不超过 $10^5$,字符串仅包含小写英文字母。
#include<bits/stdc++.h>
using namespace std;
const int N=100010;
int a[N][26],cnt[N],idx;
void fi(char str[]){
int m=0;
for(int i=0;str[i];i++){
int t=str[i]-'a';
if(!a[m][t]){
a[m][t]=++idx;
}
m=a[m][t];
}
cnt[m]++;
}
void fq(char str[]){
int m=0;
for(int i=0;str[i];i++){
int t=str[i]-'a';
if(!a[m][t]){
printf("0\n");
return;
}
m=a[m][t];
}
printf("%d\n",cnt[m]);
}
int main(){
int n;
char c,str[N];
cin>>n;
while(n--){
cin>>c>>str;
if(c=='I')fi(str);
else fq(str);
}
return 0;
}
并查集
#include<iostream>
using namespace std;
const int N=100010;
int p[N];//定义多个集合
int find(int x)
{
if(p[x]!=x) p[x]=find(p[x]);
return p[x];
}
int main()
{
int n,m;
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++) p[i]=i;
while(m--)
{
char op[2];
int a,b;
scanf("%s%d%d",op,&a,&b);
if(*op=='M') p[find(a)]=find(b);//集合合并操作
else
if(find(a)==find(b))
//如果祖宗节点一样,就输出yes
printf("Yes\n");
else
printf("No\n");
}
return 0;
}
模拟散列表
#include<bits/stdc++.h>
using namespace std;
const int N=100003;
int h[N],e[N],ne[N],idx,f;
int main(){
int n,x;
cin>>n;
string s;
memset(h,-1,sizeof(h));
while(n--){
cin>>s>>x;
int k=(x%N+N)%N;
if(s=="I"){
e[idx]=x;
ne[idx]=h[k];
h[k]=idx;
idx++;
continue;
}
k=h[k];
f=1;
while(k!=-1){
if(e[k]==x){
printf("Yes\n");
f=0;
break;
}
k=ne[k];
}
if(f)printf("No\n");
}
return 0;
}
字符串哈希
给定一个长度为 $n$ 的字符串,再给定 $m$ 个询问,每个询问包含四个整数 $l_1, r_1, l_2, r_2$,请你判断 $[l_1, r_1]$ 和 $[l_2, r_2]$ 这两个区间所包含的字符串子串是否完全相同。
字符串中只包含大小写英文字母和数字。
#include<bits/stdc++.h>
using namespace std;
typedef unsigned long long ULL;
const int N=100010,P=131;
ULL h[N],p[N];
ULL f(int l,int r){
return h[r]-h[l-1]*p[r-l+1];
}
int main(){
char s[N];
int n,m,l1,r1,l2,r2;
scanf("%d%d%s",&n,&m,s+1);
p[0]=1;
for(int i=1;i<=n;i++){
p[i]=131*p[i-1];
h[i]=h[i-1]*P+s[i];
}
while(m--){
scanf("%d%d%d%d",&l1,&r1,&l2,&r2);
if(f(l1,r1)==f(l2,r2))printf("Yes\n");
else printf("No\n");
}
return 0;
}
树状数组
给定长度为 $N$ 的数列 $A$,然后输入 $M$ 行操作指令。
第一类指令形如 C l r d
,表示把数列中第 $l \sim r$ 个数都加 $d$。
第二类指令形如 Q x
,表示询问数列中第 $x$ 个数的值。
对于每个询问,输出一个整数表示答案。
树状数组主要解决的是:
单点修改:a[k]
加上一个数。
区间求值: a[l~r]
内所有元素和。
本题加上差分即可
#include <bits/stdc++.h>
using namespace std;
const int N = 100009;
int n, m, a[N], t[N];
int low_bit(int x) {
return x & -x;
}
void add(int x,int y) {
for(;x <= n;x += low_bit(x)) t[x] += y;
return;
}
int ask(int x) {
int ans = a[x];
for(;x;x -= low_bit(x))
ans += t[x];
return ans;
}
int main() {
cin >> n >> m;
for(int i = 1;i <= n;++ i) cin >> a[i];
char ch;
for(int i = 1, l, r, d;i <= m;++ i) {
cin >> ch;
if (ch == 'C') {
cin >> l >> r >> d;
add(l, d); add(r + 1, -d);
}
if (ch == 'Q') {
cin >> d;
cout << ask(d) << endl;
}
}
return 0;
}