字符串:
P4391 [BOI2009] Radio Transmission 无线传输
https://www.luogu.com.cn/problem/P4391
kmp的next数组
如果next[x]=len(0<len<x),那么就有s[len]=s[x];
那么去掉s[x]后得到的[1,x-1]依旧是原串的循环子串,因为 x 为最短长度,所以可得 next[x]一定
为0;
所以我们可以推论得到next[x+1]=1; next[x+2]=2...
最终得到 next[n]==n-x
#include<bits/stdc++.h>
using namespace std;
const int maxn = 1e6+10;
int n,kmp[maxn];
char a[maxn];
int main(){cin>>n;for(int i = 1;i <= n;i++){cin>>a[i];}int j = 0;for(int i = 2;i <= n;i++){while(j&&a[i]!=a[j+1]){j = kmp[j];}if(a[i] == a[j+1]) j++;kmp[i] = j;}cout<<n-kmp[n]<<endl;
}
P3435 [POI2006] OKR-Periods of Words
https://www.luogu.com.cn/problem/P3435
题目中的“匹配前缀”我们可以这样理解:在A的前缀中,把这个前缀再叠加一遍后就把A包括进来,如图:
abcabcab
''''''''''>>
abcabcabcabc
那么,"abcabcab"的最长匹配子串应该是"abcabc",长度为6。
我们设第一个图中字符串为S,第二个字符串为SS,显然有S[6..8]=SS[6..8]=SS[1..2]=S[1..2]。于是我们得到规律,匹配前缀子串满足KMP算法中“前缀等于后缀”的性质,我们要使子串最长,那么这个匹配长度应该尽可能小。比如对于S来说,next[8]应该为5,表示S[1..5]和S[4..8]是匹配的,但我们选择的是最短的匹配长度short[8]=2,S[1..2]=S[7..8],而答案就是8-short[8]=6。
但是KMP只能求出每个前缀串的最长匹配长度,如果要求出最短匹配长度,我们可以一直递推next[i],next[next[i]]...,直到为0. 熟悉的KMP本质的人都应该知道为什么,这里举一个例子。
在S中,next[8]=5,而next[5]=2,next[2]=0了,所以next[5]=2就是8的最短匹配长度,将8-2累计到答案中即可。
最后,类似求next时的递推方法,我们可以递推short来提高效率。比如在上例中,我们得到short[8]=2后,就直接将next[8]修改为2,这样8以后的数字如果递推到8了就可以直接跳到next[2]=0,而不用跳到next[5]这里。
#include<bits/stdc++.h>
using namespace std;
const int maxn = 1e6+10;
int kmp[maxn];
char a[maxn];
long long ans = 0;
int n;
int main(){cin>>n;cin>>a+1;int j = 0;for(int i = 2;i <= n;i++){while(j&&a[i]!=a[j+1]) j = kmp[j];if(a[i] == a[j+1]) j++;kmp[i] = j;}j = 2;for(int i = 2;i <= n;i++){j = i;while(kmp[j]) j = kmp[j];if (kmp[i]) kmp[i] = j;ans += i-j;}cout<<ans<<endl;return 0;
}
P3805 【模板】manacher 复习一下qwq
https://www.luogu.com.cn/problem/P3805
首先,读入的字符串中应在每个有效字符之间插入无效字符,首尾字符应该不同,否则最中间字符的回文串长度会莫名其妙大1.
一开始我 WA on #8 & #16,应将 aaa 替换为 ?a?a?a?,我替换为了 a?a?a?(一定要养成看讨论区的习惯qwq)
其次,通过边界r(当前回文串可达的最右下标),mid(该回文串的对称中心)
如果一个回文串的左字串有回文,那么他的右字串中也一定有一个一样的回文串
按照这个思路
#include<bits/stdc++.h>
using namespace std;
const int maxn = 11e6+10;
char a[maxn],s[maxn<<1];
int p[maxn<<1];
int n;
int cnt = 0;
void data(){s[0] = '~';for(int i = 1;i < n*2;i++){if(i%2) s[i] = a[i/2];else s[i] = '#';}s[n*2] = '|';return;
}
inline void qr(){char c=getchar();s[0]='~',s[cnt=1]='|';while(c<'a'||c>'z') c=getchar();while(c>='a'&&c<='z') s[++cnt]=c,s[++cnt]='|',c=getchar();
}
int main(){//cin>>a;//n = strlen(a); qr();int ans = 0;//for(int i = 0;i <= n*2;i++) cout<<s[i]<<" ";for(int t = 1,r = 0,mid = 0;t <= cnt;t++){if(t<=r) p[t] = min(p[mid*2-t],r-t+1);while(s[t-p[t]]==s[t+p[t]]) p[t]++;if(t+p[t]>r) r = p[t]+t-1,mid = t;ans = max(ans,p[t]); }cout<<ans-1<<endl;
}
P3435 [POI2006] OKR-Periods of Words
https://www.luogu.com.cn/problem/P3435
其实就是模板题多求两个数组l,r;
l[i]记录在第i位左侧的最大回文串长度,r同理
最后求max(l[i],r[i]), 建议和manacher一起降绿(划掉)
其他乱七八糟的题:
P2661 [NOIP2015 提高组] 信息传递
https://www.luogu.com.cn/problem/P2661
枚举每个点求最小环
#include <iostream>
#include <cstdio>
using namespace std;
const int N = 200010;
int n, fa[N], ans = 2147483647;
int get (int x, int &cnt) { cnt ++;if (fa[x] == x) return x;else return get(fa[x], cnt);
}
int main () {cin>>n;for (int i = 1; i <= n; i ++)fa[i] = i;for (int i = 1; i <= n; i ++) {int cnt = 0, f;scanf("%d", &f);if (get(f, cnt) == i) {ans = min(ans, cnt);}elsefa[i] = f;}printf("%d", ans);return 0;
}
P4779 【模板】单源最短路径(标准版)
https://www.luogu.com.cn/problem/P4779
论本蒟蒻的知识体系到底有多千疮百孔
居然连dj都打不熟,那就多打几遍( );
#include<bits/stdc++.h>
const int maxn = 1e5+10,maxm = 2e5+10;
using namespace std;
struct edge{int to,dis,next;
};
edge e[maxm];
int head[maxn],dis[maxn],cnt;
bool vis[maxn];
int n,m,s;
void add_edge(int u,int v,int d){cnt++;e[cnt].dis = d;//权值 e[cnt].to = v;//终点 e[cnt].next = head[u];//以u为起点的上一条边的编号 head[u] = cnt;//更新return;
}
struct node{int dis,pos;bool operator <( const node &x )const{return x.dis < dis;}
};
std::priority_queue<node> q;void dijkstra(){dis[s] = 0;q.push((node){0, s});while(!q.empty()){node tmp = q.top();q.pop();int x=tmp.pos,d=tmp.dis;if(vis[x]) continue;vis[x] = true;for(int i = head[x];i;i = e[i].next){int y = e[i].to;if(dis[y]>dis[x]+e[i].dis){dis[y] = dis[x]+e[i].dis;if(!vis[y]){q.push((node){dis[y],y});}}}}}
int main(){cin>>n>>m>>s;for(int i = 1;i <= n;i++) dis[i] = 2147483647;//警钟,初始化用的n一定要放在读入后//面!,本人调了足足1/48天才看出来原来是主函数有问题qwqfor(int i = 1;i <= m;i++){int u,v,w;cin>>u>>v>>w;add_edge(u,v,w);} dijkstra();for(int i = 1;i <= n;i++){cout<<dis[i]<<" ";}return 0;
}