字符串专题1

Python专题:四、字符串(1) 字符——>数字,ASCII编码,美国信息交换标准代码,当前ASCII编码包含128个字符,其中95个为可见字符,33个为不可见字符(多数已被废弃)。英文大小写字母,数字和标点符号,但是西欧字母,假名,阿拉伯字母等字符Python就无能无力了。\n 换行符 \t横向制表符 \r回车符 \\代表\ \\\\代表\\。全宇宙统一的编码——Unicode编码,Python3默认编码,可查看。编码和解码使用的编码表不一致,导致的文章内容混乱。'' (单引号) ""(双引号)''' ''' 三引号可跨行输入。 阅读详情

.
都是些bzoj原题辣,这几天刚做的

Bzoj4032


有趣的dp题,也要用到各种自动机
注意到题目的两个关键词“子串”和“子序列”
考虑对A和B串建立后缀自动机和序列自动机

序列自动机:可以识别一个序列所有子序列的自动机

想必学过自动机的各位都知道这个玩意怎么建,这里不再阐述
让后我们考虑这些询问
询问1:直接用SAM做类似LCS的做法求出A每个前缀和B匹配的最长距离,取最小+1
询问2:枚举A串所有子串并在B的序列自动机上面跑记录最小答案
询问3:遍历B的SAM,对应在A的序列自动机上一起走,如果走到一个节点,A的转移有B以外的转移,那么返回该节点的深度,否则继续dfs
询问4:直接上bfs,记一个三维向量 ( x , y , d ) (x,y,d) (x,y,d)表示走到串A的第x个,串B的第y个,已经取出的长度为d,转移的时候枚举下一个匹配的字符即可
还是比较好理解的~

#include<queue>
#include<stdio.h>
#include<string.h>
#include<algorithm>
#define N 4010
using namespace std;
struct V{ int x,y,d; } z;
char A[N],B[N]; int n,m,l[N],f[N][N]; queue<V> q;
namespace SAM{
	int s[N][26],mx[N],f[N],cnt=1,lst=1;
	inline int extend(int c){
		int p=lst,np=lst=++cnt,q,nq;
		mx[np]=mx[p]+1;
		for(;p&&!s[p][c];p=f[p]) s[p][c]=np;
		if(!p) return f[np]=1;
		q=s[p][c];
		if(mx[q]==mx[p]+1) f[np]=q;
		else{
			nq=++cnt;
			mx[nq]=mx[p]+1;
			f[nq]=f[q]; f[q]=f[np]=nq;
			memcpy(s[nq],s[q],26<<2);
			for(;p&&s[p][c]==q;p=f[p]) s[p][c]=nq;
		}
	}
	inline void match(){
		for(int x=1,i=1,c=0;i<=n;++i){
			for(;x>1 && !s[x][A[i]-'a'];x=f[x]);
			c=mx[x];
			if(!s[x][A[i]-'a']) l[i]=0; else{
				x=s[x][A[i]-'a']; l[i]=++c;
			}
		}
	}
}
namespace AAM{
	int s[N][26];
	inline void build(){
		for(int i=1;i<=n;++i)
			for(int j=i-1;;--j){
				s[j][A[i]-'a']=i;
				if(A[j]==A[i] || !j) break;
			}
	}
}
namespace BAM{
	int s[N][26];
	inline void build(){
		for(int i=1;i<=m;++i)
			for(int j=i-1;;--j){
				s[j][B[i]-'a']=i;
				if(B[j]==B[i] || !j) break;
			}
	}
}
inline void dfs(int x,int y,int d){
	for(int i=0;i<26;++i)
		if(AAM::s[x][i]&&!SAM::s[y][i]){
			*l=min(*l,d+1); return;
		}
	for(int i=0;i<26;++i)
		if(AAM::s[x][i]&&SAM::s[y][i])
			dfs(AAM::s[x][i],SAM::s[y][i],d+1);
}
int main(){
	scanf("%s%s",A+1,B+1);
	n=strlen(A+1); m=strlen(B+1);
	for(int i=1;i<=n;++i) SAM::extend(B[i]-'a');
	SAM::match();
	AAM::build(); BAM::build();
	*l=n; for(int i=1;i<=n;++i) if(l[i]<i) *l=min(*l,l[i]+1);
	printf("%d\n",*l); *l=n;
	for(int i=1;i<=n;++i){
		int x=0;
		for(int j=i;j<=n;++j){
			x=BAM::s[x][A[j]-'a'];
			if(!x){ *l=min(*l,j-i+1); break; }
		}
	}
	printf("%d\n",*l); dfs(0,1,0);
	printf("%d\n",*l); *l=n;
	q.push((V){0,0,0});
	for(int x,y,d;!q.empty();q.pop()){
		z=q.front();
		for(int i=0;i<26;++i)if(x=AAM::s[z.x][i]){
			if(!(y=BAM::s[z.y][i])){ printf("%d\n",z.d+1); return 0; }
			else if(!f[x][y]){ f[x][y]=1; q.push((V){x,y,z.d+1}); }
		}
	}
}


Bzoj2555


无比码农的一道题,为了节省代码量,牺牲效率写了ETT
大概就是用一种数据结构维护SAM的parent树,让后查询直接在SAM上面走
网上有两种做法
1.用lct实现单点查询+链修改
2.用ETT实现单点修改+子树求和
第二个因为规模*2所以效率低下,于是成功在Bzoj倒数第二(同一页上面有yww大爷和吉老师,而且碰巧代码长度是2333B)

#include<stdio.h>
#include<string.h>
#include<algorithm>
#define N 1200010
#define R register
#define son(x) (x==s[f[x]][1])
using namespace std;
namespace SPLAY{
	int rt=0,s[N<<1][2],f[N<<1],v[N<<1],w[N<<1];
	inline void ps(int x){
		w[x]=w[s[x][0]]+w[s[x][1]]+v[x];
	}
	inline void rot(const int& x){
		R int p=f[x],g=f[p],d=son(x);
		s[p][d]=s[x][!d]; f[s[p][d]]=p;
		if(g) s[g][son(p)]=x; f[x]=g;
		s[x][!d]=p; f[p]=x; ps(x); ps(p);
	}
	inline void splay(const int& x,const int& r=0){
		for(R int p;(p=f[x])!=r;rot(x))
			if(f[p]!=r) rot(son(x)==son(p)?p:x);
		if(!r) rt=x;
	}
	inline void adp(int x,int p){
		splay(p); p+=N; splay(p,rt);
		s[x][0]=s[p][0]; f[s[p][0]]=x;
		s[x][1]=x+N; f[x+N]=x;
		s[p][0]=x; f[x]=p; splay(x+N);
	}
	inline void setp(int p,int q,int nq){
		splay(q); splay(p,rt);
		s[q][0]=nq; f[nq]=q;
		s[nq][0]=p; f[p]=nq; ps(nq); ps(q);
		p+=N; q+=N; nq+=N;
		splay(q); splay(p,rt);
		s[q][1]=nq; f[nq]=q;
		s[nq][1]=p; f[p]=nq; ps(nq); ps(q);
	}
	inline void init(){
		f[1]=N+1; s[N+1][0]=1;
	}
	inline int query(int x){
		splay(x); splay(x+N,rt);
		return v[x]+w[s[x+N][0]];
	}
}
char S[N],o[20];
int l[N],s[N][26],mx[N],sz[N],f[N];
int n,m,cnt=1,lst=1,r[N],v[N],p[N],w[N];
inline void trans(int n,int m){
	for(int i=0;i<n;++i){
		m=(m*131+i)%n;
		swap(S[i+1],S[m+1]);
	}
}
inline void extend(int c){
	int p=lst,q=s[p][c],np,nq;
	lst=np=++cnt; mx[np]=mx[p]+1; SPLAY::v[np]=1;
	for(;p&&!s[p][c];p=f[p]) s[p][c]=np;
	if(!p){ f[np]=1; SPLAY::adp(np,1); return; }
	q=s[p][c];
	if(mx[q]==mx[p]+1){ f[np]=q; SPLAY::adp(np,q); }
	else{
		nq=++cnt;
		mx[nq]=mx[p]+1;
		SPLAY::setp(f[q],q,nq);
		SPLAY::adp(np,nq);
		f[nq]=f[q]; f[q]=f[np]=nq;
		memcpy(s[nq],s[q],26<<2); 
		for(;p&&s[p][c]==q;p=f[p]) s[p][c]=nq;
	}
}
int main(){
	scanf("%d%s",&n,S+1); SPLAY::init();
	for(int j=1;S[j];++j) extend(S[j]-'A');
	for(;n--;){
		scanf("%s%s",o,S+1);
		trans(strlen(S+1),m);
		if(*o=='Q'){
			int x=1;
			for(int j=1;x&&S[j];++j) x=s[x][S[j]-'A'];
			if(x) x=SPLAY::query(x); printf("%d\n",x); m^=x;
		} else for(int j=1;S[j];++j) extend(S[j]-'A');
	}
}


Bzoj1396


这个题我以前的Blog里有,点这里即可,不过又写了一个更加高效的做法,这里就不放code了

Bzoj3473


同Bzoj3277,这里用的是广义SAM做法
不过不得不说对这种一边建树一边往父亲遍历的做法感觉不认同(好像可以卡成 O ( n 1.5 ) O(n^{1.5}) O(n1.5)),更加厉害的做法是直接用线段树合并存每个节点的right集合,据 y w w yww yww大佬说,这个复杂度 O ( n l o g 2 n ) O(n log^2n) O(nlog2n) 是对的
不过这道题没有那么多问题,因为统计的信息比较简单,所以每次往上跳每个节点最多只会被经过一次,复杂度是 O ( n ) O(n) O(n) 的
一边建自动机一边往上统计size就可以了,最后每个串在SAM上跑一次统计答案

#include<stdio.h>
#include<string.h>
#include<algorithm>
#define N 200010
using namespace std;
char S[N];
int l[N],s[N][26],mx[N],sz[N],f[N];
int n,m,cnt=1,lst=1,r[N],v[N],p[N],w[N];
inline int extend(int c){
	int p=lst,q=s[p][c],np,nq;
	if(q){
		if(mx[q]==mx[p]+1) return lst=q;
		else{
			nq=++cnt;
			mx[nq]=mx[p]+1;
			f[nq]=f[q]; f[q]=nq; 
			memcpy(s[nq],s[q],26<<2);
			sz[nq]=sz[q]; ::p[nq]=::p[q];
			for(;p&&s[p][c]==q;p=f[p]) s[p][c]=nq;
		}
		return lst=nq;
	}
	lst=np=++cnt; mx[np]=mx[p]+1;
	for(;p&&!s[p][c];p=f[p]) s[p][c]=np;
	if(!p) return f[np]=1;
	q=s[p][c];
	if(mx[q]==mx[p]+1) f[np]=q;
	else{
		nq=++cnt;
		mx[nq]=mx[p]+1;
		f[nq]=f[q]; f[q]=f[np]=nq;
		memcpy(s[nq],s[q],26<<2);
		sz[nq]=sz[q]; ::p[nq]=::p[q];
		for(;p&&s[p][c]==q;p=f[p]) s[p][c]=nq;
	}
}
int main(){
	scanf("%d%d",&n,&m);
	for(int i=1;i<=n;++i){
		scanf("%s",S+l[i]); lst=1;
		l[i+1]=l[i]+strlen(S+l[i]);
		for(int j=l[i];j<l[i+1];++j){
			extend(S[j]-'a');
			for(int x=lst;x;x=f[x])
				if(p[x]<i){ ++sz[x]; p[x]=i; } else break;
		}
	}
	for(int i=1;i<=cnt;++i) ++v[mx[i]];
	for(int i=1;i<=cnt;++i) v[i]+=v[i-1];
	for(int i=cnt;i;--i) r[v[mx[i]]--]=i;
	for(int i=1,p;i<=cnt;++i){
		p=r[i]; w[p]=w[f[p]];
		if(sz[p]>=m) w[p]+=mx[p]-mx[f[p]];
	}
	for(int i=1;i<=n;++i){
		long long A=0;
		for(int x=1,j=l[i];j<l[i+1];++j){
			x=s[x][S[j]-'a']; A+=w[x];
		}
		printf("%lld ",A);
	}
}


Bzoj3926


题目有一个非常关键的信息:叶子节点 &lt; = 20 &lt;=20 <=20个
于是可以把整颗树看成一个Trie来做,由于每条路径必然会在某一个叶子节点作为根的时候变成Trie上面的一段,于是可以对每个叶子节点为根建立一个Trie上的SAM

· 严格来说,这个树不是一个Trie,因为同一个节点的两个子节点可以是相同的字母的出边
· 但是神奇的是,广义SAM在这种情况也是试用的


这一点还是之后再尝试理解吧,相比起一般的广义SAM,还有一种写法是每次强制建立新的节点,这种写法和一般的广义SAM完全等价,不过会多用一些节点

#include<stdio.h>
#include<string.h>
#include<algorithm>
#define N 2000010 
using namespace std;
struct edge{ int v,nt; } G[N];
int s[N][11],mx[N],f[N],d[N];
int n,m,cnt=1,t=0,lst=1,c[N],h[N];
inline void adj(int x,int y){
	G[++t]=(edge){y,h[x]}; h[x]=t;
	G[++t]=(edge){x,h[y]}; h[y]=t;
}
inline int extend(int p,int c){
	int np=++cnt,q,nq;
	mx[np]=mx[p]+1;
	for(;p&&!s[p][c];p=f[p]) s[p][c]=np;
	if(!p){ f[np]=1; return np; }
	q=s[p][c];
	if(mx[q]==mx[p]+1) f[np]=q;
	else{
		nq=++cnt;
		mx[nq]=mx[p]+1;
		f[nq]=f[q]; f[q]=f[np]=nq;
		memcpy(s[nq],s[q],40);
		for(;p&&s[p][c]==q;p=f[p]) s[p][c]=nq;
	}
	return np;
}
inline int Extend(int p,int c){
	int q=s[p][c],nq;
	if(q){
		if(mx[q]==mx[p]+1) return q;
		else{
			nq=++cnt;
			mx[nq]=mx[p]+1;
			f[nq]=f[q]; f[q]=nq;
			memcpy(s[nq],s[q],40);
			for(;p&&s[p][c]==q;p=f[p]) s[p][c]=nq;
			return nq;
		}
	} return extend(p,c);
}
inline void dfs(int x,int p,int lst){
	int y=Extend(lst,c[x]);
	for(int v,i=h[x];i;i=G[i].nt)
		if((v=G[i].v)!=p) dfs(v,x,y);
}
int main(){
	scanf("%d%d",&n,&m);
	for(int i=1;i<=n;++i) scanf("%d",c+i);
	for(int x,y,i=1;i<n;++i){
		scanf("%d%d",&x,&y); 
		adj(x,y); ++d[x]; ++d[y];
	}
	for(int i=1;i<=n;++i)
		if(d[i]==1) dfs(i,0,1);
	long long A=0;
	for(int i=1;i<=cnt;++i) A+=mx[i]-mx[f[i]];
	printf("%lld\n",A);
}


Bzoj2780


这个明显就是AC自动机的活啊,为什么一定要广义SAM呢
nan道是因为模板串总长比较短?

#include<stdio.h>
#include<string.h>
#include<algorithm>
#define N 360010
using namespace std;
char S[N],c[N];
int s[N][26],f[N],lst[N],sz[N];
int n,m,l[N],cnt=1,q[N],p[N],A[N],e[N];
inline void insert(char* t,int r){
	int x=1;
	for(;*t;++t){
		if(!s[x][*t-'a'])
			s[x][*t-'a']=++cnt;
		x=s[x][*t-'a'];
	}
	if(sz[x]) e[sz[x]]=r;
	sz[x]=r;
}
inline void build(){
	int l=1,r=0;
	for(int i=0;i<26;++i)
		if(!s[1][i]) s[1][i]=1;
		else f[s[1][i]]=1,q[++r]=s[1][i];
	for(int x;l<=r;++l){
		x=q[l];
		for(int i=0;i<26;++i)
			if(!s[x][i]) s[x][i]=s[f[x]][i];
			else f[s[x][i]]=s[f[x]][i],q[++r]=s[x][i];
	}
}
int main(){
	scanf("%d%d",&n,&m);
	for(int i=1;i<=n;++i){
		scanf("%s",S+l[i]);
		l[i+1]=l[i]+strlen(S+l[i]);
	}
	for(int i=1;i<=m;++i){
		scanf("%s",c); insert(c,i);
	}
	build();
	for(int i=1;i<=n;++i){
		int x=1;
		for(int j=l[i];j<l[i+1];++j){
			x=s[x][S[j]-'a'];
			for(int y=x;y;y=f[y])
				if(p[y]==i) break; else ++A[sz[y]],p[y]=i;
		}
	}
	for(int i=m;i;--i) if(e[i]) A[i]=A[e[i]];
	for(int i=1;i<=m;++i) printf("%d\n",A[i]);
}

11.25 11.25 11.25更新

Bzoj3670


是一个KMP好题也,仿照kmp的做法就可以做啦
你问我为什么代码写的是SAM?因为我当时不会这种做法啦
不过直到今天我才用这份代码卡过去的。一直90ptsTLE
大概就是在parent树上面做倍增,每次建树只考虑那些是前缀的节点,每次扩展自动机后倍增到一个尽可能长的父亲节点,复杂度 O ( n log ⁡ n ) O(n\log n) O(nlogn)
当时真的不知道脑子在想什么写的一堆奇怪的东西,比如这里面的sz其实就是mx,算了懒得改

#pragma GCC opitmize("O3")
#pragma G++ opitmize("O3")
#include<stdio.h>
#include<string.h>
#include<algorithm>
#define N 2000010
#define M 1000000007
using namespace std;
char str[N]; long long ans=1;
int s[N][26],mx[N],sz[N],f[N];
int n,lst=1,cnt=1,a[21][N]; 
inline int extend(int c){
	register int p=lst,np=lst=++cnt,q,nq;
	mx[np]=mx[p]+1; sz[np]=1;  memset(s[cnt],0,26<<2);
	for(;p&&!s[p][c];p=f[p]) s[p][c]=np;
	if(!p) { f[np]=1; goto end; }
	q=s[p][c];
	if(mx[q]==mx[p]+1) f[np]=q;
	else{
		nq=++cnt;
		mx[nq]=mx[p]+1;
		f[nq]=f[q]; f[q]=f[np]=nq;
		memcpy(s[nq],s[q],26<<2);
		for(;p&&s[p][c]==q;p=f[p]) s[p][c]=nq;
		p=f[nq]; a[0][nq]=sz[p]?p:a[0][p];
		for(int j=1;j<=15;++j) a[j][nq]=a[j-1][a[j-1][nq]];
	}
	end:sz[np]+=sz[p=f[np]]; 
	a[0][np]=sz[p]?p:a[0][p];
	for(int j=1;j<=15;++j)
		a[j][np]=a[j-1][a[j-1][np]];
	p=np;
	for(int j=15;~j;--j)
		while((mx[a[j][p]]<<1)>mx[np])p=a[j][p];
	p=a[0][p];
	p!=np?ans=ans*(sz[p]+1)%M:0;
}
int _18520(){
	memset(sz,0,sizeof sz);
	memset(s[1],0,26<<2); 
	scanf("%s",str+1); n=strlen(str+1);
	for(int i=1;i<=n;++i) extend(str[i]-'a');
	printf("%lld\n",ans);
}
int main(){
	int T; scanf("%d",&T);
	for(;T--;_18520())ans=cnt=lst=1;
}


Codeforces471D


有点太裸了吧,差分后KMP即可。
↓↓↓↓↓↓非常特别的kmp写法↓↓↓↓↓↓

#include<stdio.h>
#include<string.h>
#include<algorithm>
#define N 200010
using namespace std;
int n,m,s[N],c[N],nt[N];
int main(){
	scanf("%d%d",&m,&n);
	for(int i=0;i<m;++i) scanf("%d",s+i);
	for(int i=0;i<n;++i) scanf("%d",c+i);
	if(n==1) return 0&printf("%d\n",m);
	if(m==1) return 0&puts("0");
	n--; m--;
	for(int i=m;i;--i) s[i]-=s[i-1];
	for(int i=n;i;--i) c[i]-=c[i-1];
	for(int i=0;i<m;++i) s[i]=s[i+1];
	for(int i=0;i<n;++i) c[i]=c[i+1];
	for(int i=1,j;i<n;++i)
		for(j=i;j;) if(c[j=nt[j]]==c[i]){ nt[i+1]=j+1; break; }
	int A=0;
	for(int i=0,j=0;i<m;++i){
		if(j<n && s[i]==c[j]) ++j;
		else while(j) if(c[j=nt[j]]==s[i]){ ++j; break; }
		if(j==n) ++A;
	}
	printf("%d\n",A);
}


Bzoj1009


非常经典的AC自动机上dp
f i , j f_{i,j} fi,j​表示长度为i,匹配到了自动机上面第j个节点的方案数
因为j很小而i很大所以用矩阵加速

#include<stdio.h>
#include<string.h>
#include<algorithm>
using namespace std;
char S[30];
int n,m,M,s[30][10],f[30],q[30];
inline void ad(int& x,int y){ x=(x+y)%M; }
struct Mat{
	int s[30][30],n,m;
	inline void set(int x,int y){
		n=x; m=y; memset(s,0,sizeof s);
	}
	inline Mat operator* (Mat b){
		Mat c; c.set(n,b.m);
		for(int i=1;i<=n;++i)
			for(int k=1;k<=m;++k)
				for(int j=1;j<=b.m;++j)
					ad(c.s[i][j],s[i][k]*b.s[k][j]);
		return c;
	}
} a,b;
int main(){
	scanf("%d%d%d%s",&n,&m,&M,S);
	int x=1;
	for(int i=0;i<m;++i) x=s[x][S[i]-48]=i+2;
	int l=1,r=0;
	for(int i=0;i<=9;++i)
		if(!s[1][i]) s[1][i]=1;
		else f[q[++r]=s[1][i]]=1;
	for(int x;l<=r;++l){
		x=q[l];
		for(int i=0;i<=9;++i)
			if(!s[x][i]) s[x][i]=s[f[x]][i];
			else f[q[++r]=s[x][i]]=s[f[x]][i];
	}
	b.set(m,m);
	for(int j=1;j<=m;++j)
		for(int k=0;k<=9;++k){
			ad(b.s[j][s[j][k]],1);
		}
	a.set(1,m);
	a.s[1][1]=1;
	for(;n;b=b*b,n>>=1) if(n&1) a=a*b;
	int A=0;
	for(int j=1;j<=m;++j) ad(A,a.s[1][j]);
	printf("%d\n",A);
}


Bzoj2085


比较有趣的一个dp?
其实还是有点难想
我们考虑求出一个矩阵 S S S这里 S i , j S_{i,j} Si,j​的定义是这样的:
最长的len使得串i长度为len的后缀=串j长度为len的前缀
反正有点像扩展kmp求的那个东西
不过我们用哈希求就可以了
接下来就可以dp了
F i , j F_{i,j} Fi,j​表示出现了i个名字,最后一个出现的名字为j的最短长度
由于互不包含,枚举下一个出现的名字k,得到转移
F i + 1 , k = m i n { F i , j + S i , j } F_{i+1,k}=min\{F_{i,j}+S_{i,j}\} Fi+1,k​=min{Fi,j​+Si,j​}拿个矩阵来加速转移就可以了

#include<stdio.h>
#include<string.h>
#include<algorithm>
#define LL long long 
using namespace std;
char S[2000100];
int n,m,l[210]; LL h[2000010],bas[2000010];
inline LL gmin(LL& x,LL y){ x>y?x=y:0; }
struct Mat{
	LL s[220][220]; int n,m;
	inline void set(int x,int y){
		n=x; m=y; memset(s,0x3f,sizeof s);
	}
	inline Mat operator* (Mat b){
		Mat c; c.set(n,b.m);
		for(int i=1;i<=n;++i)
			for(int k=1;k<=m;++k)
				for(int j=1;j<=b.m;++j)
					gmin(c.s[i][j],s[i][k]+b.s[k][j]);
		return c;
	}
} a,b;
inline LL gH(int l,int r){
	return h[r]-h[l-1]*bas[r-l+1];
}
int main(){
	l[1]=1; scanf("%d%d",&n,&m);
	for(int i=1;i<=n;++i){
		scanf("%s",S+l[i]);
		l[i+1]=l[i]+strlen(S+l[i]);
	}
	for(int i=*bas=1;i<l[n+1];++i){
		bas[i]=bas[i-1]*131;
		h[i]=h[i-1]*131+S[i];
	}
	a.set(1,n); b.set(n,n);
	for(int i=1;i<=n;++i)
		for(int j=1;j<=n;++j){
			for(int k=min(l[j+1]-l[j],l[i+1]-l[i])-2;k>=-1;--k)
				if(gH(l[j],l[j]+k)==gH(l[i+1]-1-k,l[i+1]-1)){
					b.s[i][j]=l[j+1]-l[j]-k-1;
					break;
				}
		}
	for(int i=1;i<=n;++i) a.s[1][i]=l[i+1]-l[i];
	for(--m;m;b=b*b,m>>=1) if(m&1) a=a*b;	
	LL A=1ll<<62;
	for(int i=1;i<=n;++i) gmin(A,a.s[1][i]);
	printf("%lld\n",A);
}


Codeforces494B


这也是一道dp,kmp只是辅助
先求出所有匹配的位置,记 b i b_i bi​表示i是否是一个匹配的结尾
考虑dp, F i F_i Fi​表示长度为i的方案数, S i S_i Si​表示 F 1 − F i F1-Fi F1−Fi的和
转移的方式有3种
1.舍弃当前位置
2.当前位置到上一个匹配点作为一段,舍弃前面其余的
3.当前位置到上一个匹配点作为一段,不舍弃前面其余的
那么一起就是
F i = F i − 1 + S l a s t − 1 + l a s t F_i=F_{i-1}+S_{last-1}+last Fi​=Fi−1​+Slast−1​+lastlast是上一个匹配点的开头

#include<stdio.h>
#include<string.h>
#include<algorithm>
#define N 100010
#define M 1000000007
using namespace std;
int f[N],s[N],n,m,nt[N],b[N]; char t[N],c[N];
inline void ad(int& x,int y){ x=(x+y)%M; }
int main(){
	scanf("%s%s",t,c);
	m=strlen(t); n=strlen(c);
	for(int i=1,j;i<n;++i)
		for(j=i;j;) if(c[j=nt[j]]==c[i]){ nt[i+1]=j+1; break; }
	for(int i=0,j=0;i<m;++i){
		if(j<n && t[i]==c[j]) ++j;
		else while(j) if(c[j=nt[j]]==t[i]){ ++j; break; }
		if(j==n) b[i+1]=1;
	}
	int lst=0;
	for(int i=1;i<=m;++i){
		if(b[i]) lst=i-n+1;
		f[i]=f[i-1];
		if(lst) ad(f[i],s[lst-1]+lst);
		s[i]=s[i-1]; ad(s[i],f[i]);
	}
	printf("%d\n",f[m]);
}


Bzoj1355


稍加分析,周期+border=n,求next数组即可
code(in Latex)

# i n c l u d e &lt; s t d i o . h &gt; \#include&lt;stdio.h&gt; #include<stdio.h>
u s i n g   n a m e s p a c e   s t d ; using\ namespace\ std; using namespace std;
c h a r   s [ 1000010 ] ;   i n t   n , n t [ 1000010 ] ; char\ s[1000010];\ int\ n,nt[1000010]; char s[1000010]; int n,nt[1000010];
i n t   m a i n ( ) { int\ main()\{ int main(){
s c a n f ( &quot; % d % s &quot; , &amp; n , s ) ; \quad scanf(&quot;\%d\%s&quot;,\&amp;n,s); scanf("%d%s",&n,s);
f o r ( i n t   i = 1 , j ; i &lt; n ; + + i ) \quad for(int\ i=1,j;i&lt;n;++i) for(int i=1,j;i<n;++i)
f o r ( j = i ; j ; )   i f ( s [ j = n t [ j ] ] = = s [ i ] ) {   n t [ i + 1 ] = j + 1 ;   b r e a k ;   } \quad \quad for(j=i;j;)\ if(s[j=nt[j]]==s[i])\{\ nt[i+1]=j+1;\ break;\ \} for(j=i;j;) if(s[j=nt[j]]==s[i]){ nt[i+1]=j+1; break; }
p r i n t f ( &quot; % d \ n &quot; , n − n t [ n ] ) ; \quad printf(&quot;\%d\backslash n&quot;,n-nt[n]); printf("%d\n",n−nt[n]);
} \} }

【字符串入门专题1】 E - Cyclic Nacklace hdu3764 【kmp最小循环节应用】 CC always becomes very depressed at the end of this month, he has checked his credit card yesterday, without any surprise, there are only 99.9 yuan left. he is too distressed and thinking about how 阅读详情

相关推荐

从SSTS到CTS:解码汽车电子架构中的需求与实现规范

本文深入解析汽车电子架构中SSTS(子系统功能规范)与CTS(零部件技术规范)的转换过程,揭示从抽象需求到物理实现的关键步骤。通过实际案例说明如何将SSTS的功能逻辑、接口定义和性能指标转化为具体的CTS实施方案,并分享多供应商协同与测试验证的最佳实践,帮助开发者高效完成汽车电子系统开发。

weixin_29201859的博客 498

字符串专题详解

字符串专题详解

m0_72674633的博客 1235

告别Inspect!用微软官方推荐的Accessibility Insights搞定WinApp自动化测试元素定位

本文介绍了如何从传统的Inspect工具升级到微软官方推荐的Accessibility Insights for Windows,以提升Windows应用自动化测试中的元素定位效率。通过详细的功能对比、安装指南和实战技巧,帮助测试工程师掌握这款现代化工具的核心优势,包括实时元素检测、属性筛选和与WinAppDriver、Appium等测试框架的无缝集成。

weixin_26749245的博客 617

C语言字符串(0基础到深入剖析)---字符串系列合集(函数+指针+数组)

本篇旨在帮助不了解字符串或者逻辑梳理不够透彻的伙伴们理出一条脉络。选择能看懂的部分即可,。适用范围:0基础C语言(刚学字符串)- 学过函数 - 学过指针 ---大致了解了数据内存(栈、堆、静态区、常量区) --- C语言语法大成者。只是梳理逻辑,代码相对简单,对字符串的算法题型(洛谷,力扣)不涉及。这里不在赘述字符,字符数组的东西了。因为最开始学数据类型和数组时已经。

博客网站地址: https://arsenova.xyz 我会将优快云上的博客重修一部分迁移到网站上。 文章会做同步更新,但我不会在这个糟糕的平台多待1秒。下面是我要说的话,save your time. keep going 1344

mysql 计算不同胜率的活跃玩家的次日,3日,7日留存率

它根据每天的日期(logymd)和最高段位(max_rank)、胜率段落(win_rate_segment)进行分组,并计算总玩家人数(total_players)、第二天的留存人数(next_day_players)、第三天的留存人数(day3_players)和第七天的留存人数(day7_players)。注意:不是新手留存,是段位活跃玩家留存;最终的结果则是将留存数据进行展示,按照日期(date)、最高段位(rank_level)、胜率段落(win_rate_segment)进行排序。

weixin_42698221的博客 1833

akshare 布林通道策略

import datetime import pandas as pd import backtrader as bt import matplotlib.pyplot as plt from datetime import datetime import matplotlib import akshare as ak %matplotlib inline class Boll_strategy(bt.Strategy): #自定义参数,每次买入1800手 params=(('size'.

qq_26742269的博客 5335

A - 雷同检测

考试的时候老师最讨厌有人抄袭了。自从有了电子评卷,老师要查找雷同卷,就容易多了,只要将两个人的答案输入计算机,进行逐个字符的比对,把相同的位置都找出来,就一目了然了。 输入格式 2 行,每行包含一串字符(长度不超过 200200)。 输出格式 1 行,包含若干个以空格分隔的数字,表示出现相同字符的位置 样例输入 I am suantoujun. I am huayemei. 样例输出 1 2 3 4 5 6 8 9 #include<stdio.h> #include&l.

m0_62289745的博客 696

LuaJit分析(十一)去除string.dump函数

因此即使我们的指令顺序被打乱,通过loadfile系统调用,再通过string.dump调用,可以得到字节码文件,再通过与标准的luajit生成的字节码文件比对,即可找出差异所在。Lua脚本中的string.dump函数用于生成字节码文件,根据对 luajit -b命令的分析可以得出,最终dump出字节码文件都是使用的string.dump函数。同时lj_libdef.h和lj_ffdef.h这两个文件都是自动生成的,在msvcbuild.bat中,使用如下命令生成。

a545958498的博客 1131

字符串专题

字符串 字符与字符串的区别在于字符串的末尾是’\0’ Printf()函数默认以C风格输出 拷贝复制模型 Void(char*from, char *to) //++ 的优先级比×高 { While((*from++ = *to++) &&!= ‘\0’ ) { } }

蔡曜镫专栏 612

STM32 BOOTLOADER配置以及APP跳转实现(裸机)

Bootloader是硬件启动的引导程序,是运行操作系统的前提。在操作系统内核或用户应用程序运行之前运行的一段小代码。对硬件进行相应的初始化和设定,最终为操作系统准备好环境。APP就是我们的应用程序,经过硬件引导程序之后跳转到系统程序运行。

吻等离子的博客 3355

【Unity 实用工具篇】✨| 受击插件Feel 详细教程,开发游戏时更好的操控 游戏打击感

在一个游戏中,游戏手感对于游戏的整体游玩体验是非常重要的,而游戏手感中有一个很重要的点:打击感 那本篇文章就来讲一下打击感中的受击反馈部分。 在游戏中经常会用到攻击敌人,敌人扣血,执行某些受击功能的执行,尤其是对于有战斗场景的游戏来说,这就是标准的受击反馈。 所以引出本篇文章的主角:受击插件 Feel 的使用,2D和3D游戏都可以使用。

努力前行,总会成为自己心中的那道光 3万+

字符串处理专题

codeup习题 文章目录codeup习题Problem A: 字符串连接Problem B: 首字母大写Problem C: 字符串的查找删除 Problem A: 字符串连接 要求不使用库函数把两个字符串连接并输出。 非常简单。 #include&amp;amp;amp;lt;stdio.h&amp;amp;amp;gt; int main(){ char str1[110],str2[110]; while(scanf(&amp;amp;quot;%s%s&

Xin1203的博客 614

【Linux命令大全】006.网络通讯之netstat命令(实操篇)

本文详细介绍了Linux系统中netstat网络诊断命令的实用技巧。主要内容包括:1) 基本功能与参数详解,展示如何查看网络连接、路由表和接口统计;2) 安装方法及基础用法,如查看TCP/UDP连接、显示进程信息和路由表;3) 高级应用技巧,包括监听端口检查、连接状态统计和实时监控。文章通过表格和代码示例,系统性地呈现了netstat在系统管理中的核心应用场景,是网络故障排查和性能监控的实用指南。

yuhuasheng1997的博客 705

字符串专题-1

刷题笔记

MXZSL的博客 996

IEM模型,地表粗糙度模拟

利用微波反演土壤水分时候,利用IEM模型可以实现对随机地表的模拟

visio安装包

Microsoft Office Visio 是一款专业的制图软件。利用经过更新的形状、协作工具和数据链接图表,创建专业级图表以简化复杂的信息。

1 字符串专题

pat字符串

hmsjdzzj的博客 119

LinePhone3.2.7(android端最新版本) sip客户端

SIP客户端For android:LinePhone3.2.7(最新版本),官网地址:http://www.linphone.org/

寒假集训1字符串专题

第一题 Let the Balloon Rise 链接http://acm.hdu.edu.cn/diy/contest_showproblem.php?pid=1001&cid=34727 用结构体或者map来统计某一颜色出现的最多次数 #include<iostream> #include<map> #include<string> usin...

bravo的博客 1110

字符串专题 【2014】四1

var s:string; len,i:integer; function delnum(var s: string): integer; var i,j:integer; begin j:=1; for i:=1 to length(s) do if (s[i]<'0') or (s[i]>'9'...

weixin_30896763的博客 77

字符串专题1-1

DescriptionYou, as a member of a development team for a new spell checking program, are to write a module that will check the correctness of given words using a known dictionary of all correct words i...

s_h_w_s_n_g的博客 197

字符串专题EP.1-KMP

   KMP是啥?    一种匹配算法,能够在线性时间内判定一个字符串A是否是字符串B的子串(即连续的字符),以及出现的次数。    我们由例题引入:           给定一个文本串S和一个模式串T,问模式串在文本串中出现了多少次?    首先,一个暴力的做法是枚举S中的每一位,向后与T串每一位进行匹配,匹配失败就移到下一位。复杂度是O(n*m)的    当然hash也可以在xian...

qq_40716114的博客 231

算法复健计划day1 字符串专题

好久没敲过题了,有点太拉了,为了保研机试,冲冲冲 字符串专题 ACWing1477 https://www.acwing.com/problem/content/1479/ #include <bits/stdc++.h> using namespace std; string eng[10] = {"zero","one","two","three","four","five","six","seven","eight","nine"} ; int main() { string

u011612364的博客 176

python学习记录1——字符串操作专题

python学习记录1-字符串操作学习 大小写转换 变量.upper()转大写 变量.lower()转小写 变量.title()转首字母大写 name = &amp;amp;amp;amp;quot;wfd&amp;amp;amp;amp;quot; print (name.upper()) name = &amp;amp;amp;amp;quot;WFD&amp;amp;amp;amp;quot; print (name.lower()) name = &am

wfd519的博客 190

专题一MATLAB基础知识——1.7字符串处理

字符串的表示 在MATLAB中,字符串是用单引号括起来的字符序列 若字符串中的字符含有单引号,则该单引号字符要用两个单引号来表示。 建立多行字符串,形成字符串矩阵。 例子:建立一个字符串向量,然后对该向量做如下处理: 取第1~5个字符组成的子字符串 将字符串倒过来重新排列 将字符串中的小写字母变成相应的大写字母,其余字符不变 统计字符串中小写字母的个数 字符串的操作 1、字符串的执行 eval(s):把字符串s作为对应的MATLAB命令来执行 2、字符串与数值之间的转换 字符串是以ASCII码

Ricardo2的博客 3000

【字符串入门专题1】A - 亲和串 hdu 2203 【kmp】

人随着岁数的增长是越大越聪明还是越大越笨,这是一个值得全世界科学家思考的问题,同样的问题Eddy也一直在思考,因为他在很小的时候就知道亲和串如何判断了,但是发现,现在长大了却不知道怎么去判断亲和串了,于是他只好又再一次来请教聪明且乐于助人的你来解决这个问题。  亲和串的定义是这样的:给定两个字符串s1和s2,如果能通过s1循环移位,使s2包含在s1中,那么我们就说s2 是s1的亲和串。

loving coding 372

【JZYZ集训Day2】字符串相关专题 Hash&&KMP&&Trie&&0/1Trie

字符串相关专题。Hash&&KMP&&Trie&&0/1 Trie

qq_35684989的博客 437

专题1:C语言拷贝字符串函数的推演

1.入门等级写法 int main() { char buf1[20] = "you kn0w I love you"; char buf2[20]; int i; for (i=0;*(buf1+i)!='\0';i++) { *(buf2 + i) = *(buf1 + i); } *(buf2 + i) = '\0'; printf("buf1=%s\n", buf1); printf("buf2=%s\n", buf2); return 0; } 2.字符串做函数

m0_51355635的博客 231
上一篇: 后缀数组SA-IS模板
下一篇: Latex代码测试
扩展的灰
博客等级 码龄12年 60粉丝 342原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值