首页 > 其他分享 >Living-Dream 系列笔记 第75期

Living-Dream 系列笔记 第75期

时间:2024-08-06 13:05:43浏览次数:11  
标签:Living nxt int Dream sum 75 str border dp

CF126B

朴素解法:求出原字符串的最长 border,并 kmp 匹配出出现在中间的最长 border,若没有则不断缩短 border 的长度,直到中间存在。若 border 长度减到了 \(0\),则无解。

我们通过画图来探索优化方式。

如图,蓝色部分为原串的最长 border,红色部分为蓝色部分的最长 border。

容易发现,红色部分就是我们要求的答案。

然后就做完了。具体实现细节见代码。

code
#include<bits/stdc++.h>
using namespace std;

const int N=1e6+5;
string str;
int nxt[N],nxt2[N]; 

void getnxt(string t){
	int i=0,j=-1;
	nxt[0]=-1;
	for(;i<t.size();){
		if(j==-1||t[i]==t[j])
			i++,j++,nxt[i]=j;
		else
			j=nxt[j];
	}
}
int kmp(string s,string t){
	getnxt(t); //注意还得求一遍 nxt
	int i=0,j=0;
	for(;i<s.size();){
		if(j==t.size()-1&&s[i]==t[j])
			return i-j+1; //直接 return 是为了找中间出现的,最后 return 会找到后缀
		if(j==-1||s[i]==t[j])
			i++,j++;
		else
			j=nxt[j];
	}
	return 0;
}

int main(){
	ios::sync_with_stdio(0);
	cin>>str;
	getnxt(str);
	int last=str.size()-nxt[str.size()];
	string t=str.substr(last); //后缀(即最长 border)
	memcpy(nxt2,nxt,sizeof nxt2);
	int pos=kmp(str.substr(1),t); //str.substr(1) 是为了不取到前缀
	if(pos<last){
	    if(t=="") cout<<"Just a legend",exit(0);
		cout<<t,exit(0);
	}
	pos=nxt2[nxt2[str.size()]];
	if((!pos)||(!nxt2[str.size()]))
		cout<<"Just a legend",exit(0);
	t=str.substr(0,pos);
	cout<<t;
	return 0;
}

P4824

朴素解法:每次 kmp 匹配出 \(T\) 的出现位置并删除直到 \(S\) 中不含有 \(T\) 即可。

我们通过发掘性质来探索优化方式。

观察这样一组数据:

momomoooo
moo

我们的操作方式是:

" momo 'moo' oo " -> " mo 'moo' o " -> " 'moo' " -> ""

可以发现,起始位置越靠后的子串,越先被消除,考虑使用栈进行维护。

具体的,我们记录 \(match_i\) 表示 \(S\) 中的每一个 \(i\),它要跳到的 \(T\) 中的下一个位置,这显然可以在 kmp 匹配的过程中维护。

同时,我们也用栈维护当前的 \(S\) 串。

每当一个 \(T\) 匹配成功后,栈弹出 \(\left| T \right|\) 个字符,并令 \(j\) 跳到此时栈顶所对应的 \(match\)。

code
#include<bits/stdc++.h>
using namespace std;

const int N=1e7+5;
string str1,str2;
int top,nxt[N],stk[N],match[N]; 

void getnxt(string t){
	int i=0,j=-1;
	nxt[0]=-1;
	for(;i<t.size();){
		if(j==-1||t[i]==t[j])
			i++,j++,nxt[i]=j;
		else
			j=nxt[j];
	}
}
int kmp(string s,string t){
	getnxt(t);
	int i=0,j=0;
	for(;i<s.size();){
		if(j==-1||s[i]==t[j])
			match[i]=j+1,stk[++top]=i,i++,j++;
		else
			j=nxt[j];
		if(j==t.size())
			top-=t.size(),j=match[stk[top]];
	}
}

int main(){
	ios::sync_with_stdio(0);
	cin>>str1>>str2;
	kmp(str1,str2);
	for(int i=1;i<=top;i++)
		cout<<str1[stk[i]];
	return 0;
}

P3435

首先,画图并发掘性质

如图,\(Q\) 串是 \(P\) 串的一个周期。

显然 \(Q=P-红色部分\),而要使得 \(Q\) 最长,则红色部分一定最短。结合图分析,可将题目转化为求 \(P\) 的最短 border。

如何求最短 border?接下来我们专门研究 \(P\) 串。

如图,我不停地求出 \(P\) 串的 最长 border 的最长 border 的最长 border ...... 如此循环往复,容易发现最长 border 总是成对出现,且一定会有两个分别在 \(P\) 串的一头一尾。这样便一定能求出 \(P\) 的最短 border。

但是一步一步跳太慢了,我们加个记忆化即可。

code
#include<bits/stdc++.h>
#define int long long
using namespace std;

const int N=1e7+5;
int n,nxt[N];
string str;

void getnxt(string t){
	int i=0,j=-1;
	nxt[0]=-1;
	for(;i<t.size();){
		if(j==-1||t[i]==t[j])
			i++,j++,nxt[i]=j;
		else
			j=nxt[j];
	}
}

signed main(){
	ios::sync_with_stdio(0);
	cin>>n>>str;
	int ans=0;
	getnxt(str);
	for(int i=2,j=2;i<=n;i++){
		j=i;
		while(nxt[j])
			j=nxt[j];
		if(nxt[i]!=0)
			nxt[i]=j; //记忆化
		ans+=i-j;
	}
	cout<<ans;
	return 0;
}

CF494B

参考了 RainFestival 的题解。/bx/bx/bx

求方案数却不求具体方案,考虑 dp。

令 \(dp_i\) 表示分割方案的最后一个子串以 \(s_i\) 结尾的方案数(结尾定义状态)。

初始:\(dp_0=1\)(一个字符也有一种方案)。

转移:

\[dp_i=\sum_{j=1}^{i} \sum_{k=0}^{j-1}[t \in s_{j \sim i}]dp_k \]

\([x]\) 表示在条件 \(x\) 满足的情况下。

\(s \in t\) 表示 \(s\) 为 \(t\) 的子串。

\(i\) 满足 \(i \in [1,n]\)。

朴素转移是 \(O(n^3)\) 的(需要预处理 \([t \in s_{j \sim i}]\))。

容易发现,若在每个 \(i\) 之前找到一个最大的 \(y\) 使得 \(y+\left|t\right|-1 \le i\) 且 \(t \in s_{y \sim y+\left|t\right|-1}\),则所有满足 \(j \le y\) 的 \(j\) 才会满足 \(t \in s_{j \sim i}\)。其中 \(y\) 可以使用 kmp 求出。

于是转移变为:

\[dp_i=\sum_{j=1}^{d_i} \sum_{k=0}^{j-1}dp_k \]

还是 \(O(n^3)\),但快了一点。

考虑将式子变形。

\[dp_i=\sum_{j=1}^{d_i} \sum_{k=0}^{j-1}dp_k \]

\[dp_i=\sum_{j=0}^{d_i-1} \sum_{k=j+1}^{d_i}dp_k \]

(将掌管求和下标的求和符号提到里面来,方便化简)

\[dp_i=\sum_{j=0}^{d_i-1} dp_j \times (d_i-j) \]

(化简求和符号)

令:

\[\begin{cases} f_i=\sum_{j=0}^{i} dp_j\\ s_i=\sum_{j=0}^{i} dp_j \times j \end{cases} \]

则:

\[dp_i=d_i \times f_{d_i-1}-s_{d_i-1} \]

初始:

\[\begin{cases} f_1=1\\ s_1=0\\ dp_1=1 \end{cases} \]

code
#include<bits/stdc++.h>
#define int long long
using namespace std;

const int N=1e5+5;
const int MOD=1e9+7;
string s,t;
int d[N],tag[N],nxt[N];
int dp[N],ff[N],ss[N];

void getnxt(string y){
	int i=0,j=-1;
	nxt[0]=-1;
	for(;i<y.size();){
		if(j==-1||y[i]==y[j])
			i++,j++,nxt[i]=j;
		else
			j=nxt[j];	
	}
}
void kmp(string x,string y){
	getnxt(y);
	int i=0,j=0;
	for(;i<x.size();){
		if(j==-1||x[i]==y[j])
			i++,j++;
		else
			j=nxt[j];
		if(j==y.size())
			tag[i]=1,j=nxt[j];
	}
}

signed main(){
	ios::sync_with_stdio(0);
	cin>>s>>t;
	kmp(s,t);
	d[0]=0;
	for(int i=1;i<=s.size();i++)
		d[i]=(tag[i]?i-t.size()+1:d[i-1]);
	dp[0]=1,ff[0]=1,ss[0]=0;
	for(int i=1;i<=s.size();i++){
		if(!d[i]) dp[i]=0;
		else dp[i]=((ff[d[i]-1]*d[i]%MOD-ss[d[i]-1])%MOD+MOD)%MOD;
		ff[i]=(ff[i-1]+dp[i])%MOD;
		ss[i]=(ss[i-1]+dp[i]*i%MOD)%MOD;
	}
	cout<<((ff[s.size()]-1)%MOD+MOD)%MOD; //这里减一是因为 ff 和 dp 的初始值都是一,转移时会重复加
	return 0;
}

标签:Living,nxt,int,Dream,sum,75,str,border,dp
From: https://www.cnblogs.com/XOF-0-0/p/18344940

相关文章

  • 浮点数的表示及IEEE754标准
    浮点数的表示浮点数的规格化IEEE754标准移码IEEE754这里有一个需要特别注意的地方,IEEE754中,尾数个位上的1是隐含的IEEE的阶码保留了全0和全1来表示特殊的状态,所以阶码最大值的真值为127,对应机器数为11111110,阶码最小值的真值为-126,对应的机器数为00000001......
  • Living-Dream 系列笔记 第74期
    Kobe-Morris-Pratt算法定义一些基本定义:border:是一个字符串的子串(不与其本身相同)且满足既是其前缀又是其后缀的字符串,我们称之为该字符串的一个border。Kobe-Morris-Pratt算法(以下简称KMP算法),是解决字符串匹配问题的一种算法,实际做题中常偏思维,通常用到的只有其中的bo......
  • 【网站项目】SpringBoot758校园摄影作品评比系统
    ......
  • LeetCode 75 颜色分类
    题目描述给定一个包含红色、白色和蓝色、共n个元素的数组nums,原地对它们进行排序,使得相同颜色的元素相邻,并按照红色、白色、蓝色顺序排列。我们使用整数0、1和2分别表示红色、白色和蓝色。必须在不使用库内置的sort函数的情况下解决这个问题。思路可以使用单个指......
  • 15.75.【C语言】表达式求值
    目录一.整型提升1.定义2.一.整型提升1.定义C语言中整型算术运算总是至少以缺省(默认)整型类型的精度来进行的。为了获得这个精度,表达式中的字符和短整型操作数在使用之前被转换为普通整型,这种转换称为整型提升2.整型提升的原因:表达式的整型运算要在CPU的相应运算器件......
  • SSM 大学食堂订餐系统APP 毕业毕设-附源码75418
                      摘要本论文主要论述了如何使用SSM框架开发一个大学食堂订餐系统APP,将严格按照软件开发流程进行各个阶段的工作,面向对象编程思想进行项目开发。在引言中,作者将论述大学食堂订餐系统APP的当前背景以及系统开发的目的,后续章节......
  • 支撑英雄联盟750万同时在线用户的聊天系统底层的CRDT是什么?
    CRDT是什么意思?CRDT是Conflict-FreeReplicatedDataTypes的缩写,直译的话即“无冲突可复制数据类型”。翻译过来还是一脸懵逼!用稍微通俗一点的话说:研究分布式系统,尤其是研究最终一致性分布式系统的过程中,一个最基本的问题就是,应该采用什么样的数据结构来保证最终一致性?CR......
  • 许少辉『簡體書』乡村振兴战略下传统村落文化旅游设计(ISBN):9787112275083 七一新书辉
    许少辉『簡體書』乡村振兴战略下传统村落文化旅游设计(ISBN):9787112275083七一新书辉少许『簡體書』乡村振兴战略下传统村落文化旅游设计分類:簡體書→大陸圖書→建筑→建筑科学作者:许少辉國際書號(ISBN):9787112275083出版社:中国建筑工业出版社出版日期:2022-07-......
  • Luogu P5005 中国象棋 - 摆上马 / Luogu P8756 国际象棋 题解 [ 蓝 ] [ 状压 dp ] [
    国际象棋:模板棋盘状压。摆上马:需要点思维的棋盘状压,相比上一道题加了“蹩马脚”的设定。Easy_version:国际象棋概述一下此类棋盘问题的思路:用二进制数表示出棋盘上某一行的状态。用位运算预处理出合法的单行状态,以及需要用到的一些东西。用位运算判断前一行或者前几行能否......
  • Living-Dream 系列笔记 第71期
    众所周知,换根dp是非常套路的。换根真好玩(换根dp:当不同节点作为根时,dp结果不一致,若枚举每个节点作为根,则时间复杂度过高,在此种情形下,可使用换根dp处理相邻两节点间的贡献,从而达到快速换根的效果。使用场景:对于一棵树,寻找以某节点\(u\)为根时取得的最大值/最小值......