oier刷题笔记2024/7/4
字符串:
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);
}else
fa[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天才看出来原来是主函数有问题qwq
for(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;
}