串匹配
对若干个字符串前后缀匹配的问题一般进行预处理
对已知单词数量的字符串固定单词的字符串 s[n] 每次cin就可以了
for (int i = 0; i < n; i ++ ){
for (int j = 0; j < n; j ++ ){//若干个字符串 匹配的预处理操作
使用答案和新产生的string中取得的最大值 需要int转化 否则不通过
ans=max(ans,(int)string.size() )
kmp
#include <iostream>
using namespace std;
const int N = 100010, M = 1000010;
int n, m;
int ne[N];
char s[M], p[N];
int main()
{
cin >> n >> p + 1 >> m >> s + 1;//这里做了处理 在+1处改变
//ne[j ] 记录 最大的长度 长度是指一个字符串 长度为j 的前缀(从1-j) 和 后缀(从末尾前长度个字母到末尾)匹配的 最长不可以超过本身
//于是不满足的话 直接扔前缀过去就可以了 让前缀和后缀成功对齐 j从ne【j】
//两个下标从1开始
for (int i = 2, j = 0; i <= n; i ++ )//对模式串操作 先求next数组 从2开始才有意义 i一直再动 只有j往回走
{
while (j && p[i] != p[j + 1]) j = ne[j];//j不为0否则j=ne【j】会死循环 i和j需要一样 while是必要的不相等 j匹配下一个可能值可能是ne[j] 如果不是就是ne[ne[j]]
//先看下面
//下面的j+1是唯一将j从下标0解放出来的东西 所以j+1可以表示当前比较的下表 而j表示长度
if (p[i] == p[j + 1]) j ++ ;//j本来就是值=指的是前面的长度 这时候模式串相等就说明还可以加一个长度
ne[i] = j;//之所以不把这步和上面一起变成ne[i]=++j 是因为if
}
for (int i = 1, j = 0; i <= m; i ++ )//这里i从0 因为不是求ne数组 他的值是有意义的 j后退了 但是作为比较的i是不动的
{
while (j && s[i] != p[j + 1]) j = ne[j];//不匹配 让子串指针j 不一致回到ne[j]的位置
if (s[i] == p[j + 1]) j ++ ;//剩下 如果匹配成功 让j往后走
if (j == n)//n是子串的长度j成功走到这里子串的最后 字串成功匹配到最后 说明找到了一个匹配的这个位置 这里比上面多了一步处
{
printf("%d ", i - n);//输出位置,这里n很关键 但是i不动 找下一个
j = ne[j];//j返回,重新匹配
}
}
return 0;
}
链接:https://ac.nowcoder.com/acm/contest/23479/A
来源:牛客网
小红很喜欢红色(用'R'字母表示),但她非常讨厌紫色(用'P'字母表示)。
她想取一个连续子串,该子串包含至少 k 个'R'字符,且不能包含'P'字符。
你能告诉她有多少合法的方案可以取到吗?
注:只要连续子串的起始位置或终止位置不同,我们就认为是两个不同的方案。
识别到p就切割 问题转换成 统计满足k个p的字串有多少个
记录一个temp的数组
因为是子串 所以右端到左端满足k个的时候,就将
左端点向右走几次 就说明这个子串(右端点固定,左端点长度,长度减少的字串的数量)有几个
如 **rrr 区间不断减少
rrr
rrr
满足条件的字串有两个 为j走过的(减少的)数量
res+=j;
#include<bits/stdc++.h>
using namespace std;
int n,k;
string s;
long long res=0;
void gao(string t){ //求字符串t中有多少子串包含至少k个'R'
int i,j=0,temp=0;
for(i=0;i<t.length();i++){//枚举右端点,找第一个不合法的左端点
temp+=t[i]=='R';
while(j<=i&&temp==k){
temp-=t[j++]=='R';
}
//在这个左端点前面的都是合法的。
res+=j;
}
}
int main(){
int i;
cin>>n>>k>>s;
string t="";
for(i=0;i<n;i++){
if(s[i]=='P')gao(t),t="";
else t+=s[i];
}
gao(t);
cout<<res;
}
一个从0开始的不需要j=-1和初始化的版本~
#include <bits/stdc++.h>
using namespace std;
const int N = 100010;
int ne[N];
int n, m;
string p, s;
int main(){
cin >> n >> p >> m >> s;
//i是代匹配位置,j是以匹配长度,j - 1是以匹配的最后一个位置
//与自身匹配i从1开始
for(int i = 1, j = 0; i < n; i ++){
while(j && p[i] != p[j]) j = ne[j - 1];
if(p[i] == p[j]) j ++;
ne[i] = j;
}
//与长字符串匹配i从0开始
for(int i = 0, j = 0; i < m; i ++){
while(j && s[i] != p[j]) j = ne[j - 1];
if(s[i] == p[j]){
j ++;
if(j == n){
cout << i - n + 1 << " ";
j = ne[n - 1];
}
}
}
return 0;
}
字典树 https://www.acwing.com/problem/content/837/ 关键在于idx
#include <iostream>
using namespace std;
const int N = 100010;
int son[N][26], cnt[N], idx;//son[N]是idx数 cnt统计这个idx个数
char str[N];
void insert(char *str)
{
int p = 0;
for (int i = 0; str[i]; i ++ )
{
int u = str[i] - 'a';//
if (!son[p][u]) son[p][u] = ++ idx;//没有就新建 son存储的是idx的儿子 也是idx
p = son[p][u];//p是儿子 变成idx
}
cnt[p] ++ ;
}
int query(char *str)
{
int p = 0;
for (int i = 0; str[i]; i ++ )
{
int u = str[i] - 'a';
if (!son[p][u]) return 0;
p = son[p][u];
}
return cnt[p];//这里的p不是某个字母 而是存储到的idx
}
int main()
{
int n;
scanf("%d", &n);
while (n -- )
{
char op[2];
scanf("%s%s", op, str);
if (*op == 'I') insert(str);
else printf("%d\n", query(str));
}
return 0;
}
最大异或对https://www.acwing.com/video/63/
对于每个数 找到一个能提供最多 每位是相反数的数 另答案有最多的1就是最大的
于是把数字转化成二进制 先存最高位
#include <iostream>
#include <algorithm>
using namespace std;
const int N = 100010, M = 3100010;
int n;
int a[N], son[M][2], idx;
void insert(int x)
{
int p = 0;
for (int i = 30; i >= 0; i -- )
{
int &s = son[p][x >> i & 1];//传引用son 为了下面方便操作s 也可以写成int s=x>>i &i; if(!son[p][s]) son[p][s]=++idx;p=son[p][s];
if (!s) s = ++ idx;
p = s;
}
}
int search(int x)
{
int p = 0, res = 0;
for (int i = 30; i >= 0; i -- )
{
int s = x >> i & 1;
if (son[p][!s])//找到和他相反的
{
res += 1 << i;//贡献一个i位的1
p = son[p][!s];
}
else p = son[p][s];
}
return res;
}
int main()
{
scanf("%d", &n);
for (int i = 0; i < n; i ++ )
{
scanf("%d", &a[i]);
insert(a[i]);
}
int res = 0;
for (int i = 0; i < n; i ++ ) res = max(res, search(a[i]));
printf("%d\n", res);
return 0;
}
蓝桥杯:交换瓶子https://www.acwing.com/problem/content/1226/
实际上是 找环 :
利用st标记有没有在环里面
每次j=a[j] 将下一个点加到点里面
int main()
{
cin>>n;
int cnt=0;
for(int i=1;i<=n;i++) cin>>a[i];
for(int i=1;i<=n;i++){
if(!st[i]){
for (int j = i; !st[j]; j=a[j] ){
st[j]=true;
}
cnt++;
}
}
// cout << cnt;
cout<<n-cnt<<endl;
return 0;
}
出现次数 https://www.acwing.com/problem/content/4315/
每次询问给一个区间[l,r],问我们T在S的子串(str)S[l,r](前缀和)中出现了几次
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 1010;
int n, m, Q;
string S, T;
int s[N];
int main()
{
scanf("%d%d%d", &n, &m, &Q);
scanf("%s%s", strs, strt);
S = strs, T = strt;
S = ' ' + S;
for (int i = m; i <= n; i ++ )//选择终点表示串
{
s[i] = s[i -1 ];
if (S.substr(i - (m-1), m) == T)//看看这个长度的字串是不是满足 substr( 起始点,长度) i本身算一个字符串
s[i] ++ ;
}
while (Q -- )
{
int l, r;
scanf("%d%d", &l, &r);
l += (m - 1);//l这里的是l是前缀和的l代表串的个数 l这里本身算一个字符 所以剩下的是m-1个字符 才可以代表
if (l > r) puts("0");
else printf("%d\n", s[r] - s[l - 1]);//
}
return 0;
}
字串变化
一个字符有n个字符进行操作 查询得到相应的字符
#include <iostream>
#include <cstring>
#include <algorithm>
#include <unordered_map>
#include <queue>
using namespace std;
const int N = 6;
int n;
string A,B;
string a[N],b[N];
queue<string >qa,qb;
unordered_map<string ,int>da,db;
int extend(queue<string>& q,unordered_map<string ,int >&da ,unordered_map<string ,int>&db,string a[],string b[]){
int d=da[q.front()];
while(q.size()&&da[q.front()]==d){
auto t=q.front() ;
q.pop();
for (int i = 0; i < n; i ++ ){
for (int j = 0; j <t.size() ; j ++ ){
if(t.substr(j,a[i].size())==a[i]){
string r=t.substr(0,j)+b[i]+t.substr(j+a[i].size());
if(db.count(r)) return da[t]+db[r]+1;
if(da.count(r)) continue;
q.push(r);
da[r]=da[t]+1;
}
}
}
}
return 11;
}
int bfs(){
if(A==B) return 0;
qa.push(A);qb.push( B);
da[A]=0,db[B]=0;
int step=0;
while(qa.size()&&qb.size()){
int t;
if(qa.size()<qb.size()) t=extend(qa , da,db , a,b );
else t=extend( qb,db ,da ,b ,a);
if(t<=10) return t;
if(++step==10) return -1;
}
return -1;
}
int main()
{
cin >> A>>B;
while(cin>>a[n]>>b[n]) n++;
int t=bfs();
if(t==-1) cout << "NO ANSWER!"<<endl;
else cout << t<<endl;
return 0;
}
单词接龙
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 21;
int n,m;
string word[N];
int ans;
int used[N];
int g[N][N];
int dfs(string dragon,int last){
ans=max((int)dragon.size(),ans);
used[last]++;
for(int i=0;i<n;i++){
if(g[last][i]&&used[i]<2){
dfs(dragon+word[i].substr(g[last][i]),i);
}
}
used[last]--;
}
int main()
{
cin >> n;
char start;
for (int i = 0; i < n; i ++ ){
cin >> word[i];
}
cin >> start;
for (int i = 0; i < n; i ++ ){
for (int j = 0; j < n; j ++ ){//若干个字符串 匹配的预处理操作
for (int k = 1; k <min(//最小长度 word[i].size(),word[j].size()); k ++ )
if(word[i].substr( word[i].size()-k,k ) == word[j].substr( 0,k)) {
g[i][j]=k;
break;
}
}
}
for (int i = 0; i < n; i ++ ){
if(word[i][0]==start)
dfs(word[i],i);
}
cout << ans;
return 0;
}
牛年 https://www.acwing.com/problem/content/3373/
给出一个原点
给出固定单词数的句话 只有某三个单词可以有用
通过这个单词获得各个点的差值
#include <iostream>
#include <cstring>
#include <algorithm>
#include <unordered_map>
using namespace std;
unordered_map<string, int> id = {
{"Ox", 0}, {"Tiger", 1}, {"Rabbit", 2},
{"Dragon", 3}, {"Snake", 4}, {"Horse", 5},
{"Goat", 6}, {"Monkey", 7}, {"Rooster", 8},
{"Dog", 9}, {"Pig", 10}, {"Rat", 11}
};
int main()
{
unordered_map<string, int> p;
p["Bessie"] = 0;//p[string]表示string 和某个单词差距是多少
int n;
cin >> n;
while (n -- )
{
string s[8];//获得每一句话的各个单词
for (int i = 0; i < 8; i ++ ) cin >> s[i];//
if (s[3] == "previous")//画个数轴说明上一个年份在当前的年份的左边
{
int x = p[s[7]], y = id[s[4]];//x是上一个牛相差多少年 y是多少年
int r = ((x - y) % 12 + 12) % 12;//r是这个牛 和 过了y年之后 距离s相差多少年
if (!r) r = 12;//r是0说明相差12年 就要换回去12
p[s[0]] = x - r;//画条数轴 s[0]年份在x左边
}
else
{
int x = p[s[7]], y = id[s[4]];
int r = ((y - x) % 12 + 12) % 12;
if (!r) r = 12;
p[s[0]] = x + r;
}
}
cout << abs(p["Elsie"]) << endl;
return 0;
}