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;
}
posted @ 2024-07-04 21:38  Dreamers_Seve  阅读(16)  评论(0编辑  收藏  举报