首页 > 其他分享 >字符串

字符串

时间:2023-12-21 19:15:35浏览次数:33  
标签:nxt typedef int char 字符串 include

字符串

字符串匹配问题

在字符串s中查找某个字符串p是否出现

朴素做法

枚举s中每个长度为m的子串,然后判断这些子串和p一不一样

怎么判断一不一样?

  • 一位一位比较,这时总的复杂度为\(O(nm)\)
  • 字符串哈希优化,使用哈希可以做到\(O(n+m)\)的时间复杂度
  • KMP用线性复杂度解决字符串匹配问题
现在把i向右移动一位会怎样? - j不等于m且s[i+1]和p[j+1]一样,j也向右移动一位 - 否则j向前回到满足串s[i-k+1]$\dots$s[i]和p[1]$\dots$p[k]完全相等,且k的值最大的位置,然后继续判断 - 如果s[i+1]和p[j+1]仍不相同,则不停前退直至s[i+1]和p[j+1]相同或者j等于0为止

如何快速求出k?

  • k与s无关
  • 我们要求的就是最大的k满足k<j,使得p[1]\(\dots\)p[k]和p[j-k+1]\(\dots\)p[j]完全相同
  • 使用next数组维护每个j对应的k

例题

```cpp #include #include #include #include #include #include #include #include #include #include using namespace std; #define x first #define y second typedef pair<int, int=""> PII; typedef long long LL; const int N=1e5+10; int T,nxt[N],res,f[N]; char s[N], p[N]; void kmp_pre(char x[],int m,int nxt[]) { int i,j; j=nxt[0]=-1; i=0; while(i<m) {="" while(-1!="j&&x[i]!=x[j])" j="nxt[j];" nxt[++i]="++j;" }="" int="" kmp_count(char="" x[],int="" m,char="" y[],int="" n)="" i,j;="" ans="0;" kmp_pre(x,m,nxt);="" i="j=0;" while(i<n)="" &&="" y[i]="" !="x[j])" i++,j++;="" if(j="">=m) { f[ans]=i - m +1; ans++; j=nxt[j]; } } if(ans) return ans; return -1; } void output(char x[]) { for(int i=0,len=strlen(x);i<len;i++) cout<<x[i];="" cout<<endl;="" }="" int="" main()="" {="" cin="">>T; while(T--) { // cin>>s>>p; scanf("%s%s",s,p); res=KMP_Count(p,strlen(p),s,strlen(s)); if(res != -1) { cout<<res<<endl; for(int="" i="0;i<res;i++)" cout<<f[i]<<"="" ";="" cout<<endl;="" }="" else="" {="" puts("-1");="" <pre="">return 0;

}


2. 
<img src="https://raw.githubusercontent.com/XuandYu000/picture/main/20230826104627.png"/>

分析:答案为$n-next[n]$,一个字符串有着相同的前缀和后缀,此时将字符串长度减去最长公共前后缀长度所得到的就是一个循环覆盖,循环覆盖最小那么减去的最长公共前后缀最大。

```cpp
#include <iostream>
#include <cstring>
#include <queue>
#include <algorithm>
#include <cmath>
#include <stack>
#include <vector>
#include <map>
#include <set>
#include <array>
using namespace std;
#define x first
#define y second
typedef pair<int, int> PII;
typedef long long LL;
const int N=1e5+10;
char s[N];
int nxt[N];
void pre_kmp(char x[],int m)
{
	int i,j;
	j=nxt[0]=-1;
	i=0;
	while(i<m)
	{
		while(-1!=j&&x[i]!=x[j]) j=nxt[j];
		nxt[++i]=++j;
	}	
}
void output(int a[],int m)
{
	for(int i=0;i<=m;i++) cout<<a[i]<<" ";
	cout<<endl;
}
int main()
{
	ios::sync_with_stdio(false);
	cin.tie(0);
	scanf("%s",s);
	pre_kmp(s,strlen(s));
	// output(nxt,strlen(s));
	printf("%d",strlen(s)-nxt[strlen(s)]);

	return 0;
}

分析:将字符串加上#然后把原字符串翻转接入,求#之后最大的nxt[i]即为所求字符串的长度len,最后倒着输出前len个字符串即可

#include <iostream>
#include <cstring>
#include <queue>
#include <algorithm>
#include <cmath>
#include <stack>
#include <vector>
#include <map>
#include <set>
#include <array>
using namespace std;
#define x first
#define y second
typedef pair<int, int> PII;
typedef long long LL;
const int N=1e5+10;
char s[N];
int nxt[N];
void pre_kmp(char x[],int m)
{
	int i,j;
	j=nxt[0]=-1;
	i=0;
	while(i<m)
	{
		while(-1!=j&&x[i]!=x[j]) j=nxt[j];
		nxt[++i]=++j;
	}	
}
void output(int a[],int m)
{
	for(int i=0;i<=m;i++) cout<<a[i]<<" ";
	cout<<endl;
}
int main()
{
	ios::sync_with_stdio(false);
	cin.tie(0);
	scanf("%s",s);
	int len=strlen(s);
	s[len]='#';
	for(int i=len+1,j=len-1;j>=0;j--,i++) s[i]=s[j];
	// printf("%s",s);
	pre_kmp(s,strlen(s));
	// output(nxt,strlen(s));
	int res=0;
	for(int i=len+1;i<strlen(s);i++) res=max(res,nxt[i]);
	for(int i=res-1;i>=0;i--) cout<<s[i];
	cout<<endl;

	return 0;
}

扩展kmp

解决问题:从字符串s第i位出发,可以最多匹配多少位p中的字符

朴素 \(O(nm)\)

直接从s的i位开始,p的0位按位找

扩展kmp(Z algorithm) \(O(n)\)

以线性时间复杂度求出一个字符串s和它的任意后缀\(s[i]\dots s[n]\)的最长公共前缀的长度

  • 注意扩展kmp与kmp求出next数组的区别,前者是从字符s[i]开始,后者是从字符s[i]结束

算法流程:
计算完前i-1个z函数,维护盒子[l,r],s[l,r]=s[1,r-l+1]

  1. 如果\(i<=r\)(在盒内),则有s[i,r]=s[i-l+1,r-l+1]
    • 若z[i-l+1]<r-i+1,则z[i]=z[i-l+1]
    • z[i-l+1]>=r-i+1,则z[i]=r-i+1,从r+1后暴力枚举
  2. 如果\(i>r\)(在盒外),则从i开始暴力枚举
  3. 求出z[i]后,若i+z[i]-1>r,则更新l=i,r=i+z[i]-1

模板:

// nxt[i]:x[i……m-1]与x[0……m-1]的最长公共前缀
//extend[i]:y[i……n-1]与x[0……m-1]的最长公共前缀
void pre_KMP(char x[],int m,int nxt[])
{
	nxt[0]=m;
	int j=0;
	while(j+1<m&&x[j]==x[j+1]) j++; //找x[1]与x[0……m-1]的最长公共前缀 
	nxt[1]=j;
	int k=1;
	for(int i=2;i<m;i++)
	{
		int p=nxt[k]+k-1; //盒子右边界
		int L=nxt[i-k]; //盒子最左侧的的最长公共前缀
		if(i+L<p+1) nxt[i]=L; // 在盒内且该点开始nxt最右侧位置并没有超过盒子最右侧
		else //在盒外或者该点nxt的范围已经超过盒子的右侧
		{
			j=max(0,p-i+1);
			while(i+j<m&&x[i+j]==x[j]) j++;
			nxt[i]=j;
			k=i;
		}
	}
}
void EKMP(char x[],int m,char y[],int n,int nxt[],int extend[])
{
	pre_KMP(x,m,nxt);
	int j=0;
	while(j<n&&j<m&&x[j]==y[j]) j++;
	extend[0]=j;
	int k=0;
	for(int i=1;i<n;i++)
	{
		int p=extend[k]+k-1;
		int L=nxt[i-k];
		if(i+L<p+1) extend[i]=L;
		else
		{
			j=max(0,p-i+1);
			while(i+j<n&&j<m&&y[i+j]==x[j]) j++;
			extend[i]=j;
			k=j;
		}
	}
}

例题

分析:利用z函数的性质找到extend[i]等于模式串长度的位置即可 ```cpp #include #include #include #include #include #include #include #include #include #include using namespace std; #define x first #define y second typedef pair<int, int=""> PII; typedef long long LL; const int N=1e6+10; int T; char s[N],p[N]; int nxt[N],extend[N],lens,lenp; void pre_KMP(char x[],int m,int nxt[]) { nxt[0]=m; int j=0; while(j+1<m&&x[j]==x[j+1]) j++;="" nxt[1]="j;" int="" k="1;" for(int="" i="2;i<m;i++)" {="" p="nxt[k]+k-1;" l="nxt[i-k];" if(i+l<p+1)="" nxt[i]="L;" else="" j="max(0,p-i+1);" while(i+j<m&&x[i+j]="=x[j])" }="" void="" ekmp(char="" x[],int="" m,char="" y[],int="" n,int="" nxt[],int="" extend[])="" pre_kmp(x,m,nxt);="" while(j<n&&j<m&&x[j]="=y[j])" extend[0]="j;" extend[i]="L;" while(i+j<n&&j<m&&y[i+j]="=x[j])" main()="" scanf("%d",&t);="" while(t--)="" vector<int=""> q; scanf("%s%s",s,p); lens=strlen(s),lenp=strlen(p); EKMP(p,lenp,s,lens,nxt,extend); int res=0; for(int i=0;i<lens;i++) {="" if(extend[i]="=lenp)" res++;="" q.push_back(i);="" }="" if(res)="" cout<<res<<endl;="" for(int="" i="0,len=q.size();i<len;i++)" cout<<q[i]+1<<"="" ";="" cout<<endl;="" else="" printf("-1\n-1\n");="" <pre="">return 0;

}


2. 
<img src="https://raw.githubusercontent.com/XuandYu000/picture/main/20230826171515.png"/>
分析:找到p即是s前缀又是s后缀只需当前位置i加上nxt[i]为字符串长度即可,至于以非前后缀出现过则要求存在nxt[i]大于求得的最长位置即可
```cpp
#include <iostream>
#include <cstring>
#include <queue>
#include <algorithm>
#include <cmath>
#include <stack>
#include <vector>
#include <map>
#include <set>
#include <array>
using namespace std;
#define x first
#define y second
typedef pair<int, int> PII;
typedef long long LL;
const int N=1e6+10;
char s[N];
int len,nxt[N];
void pre_EKMP(char x[],int m,int nxt[])
{
	nxt[0]=m;
	int j=0;
	while(j+1<m&&x[j]==x[j+1]) j++;
	nxt[1]=j;
	int k=1;
	for(int i=2;i<m;i++)
	{
		int p=nxt[k]+k-1;
		int L=nxt[i-k];
		if(i+L<p+1) nxt[i]=L;
		else
		{
			j=max(0,p-j+1);
			while(i+j<m&&x[i+j]==x[j]) j++;
			nxt[i]=j;
			k=i;
		}
	}
	int res=0,maxn=0;
	for(int i=0;i<m;i++)
	{
		if(i+nxt[i]==m)
			if(maxn>=nxt[i])
				res=max(res,nxt[i]);
		maxn=max(maxn,nxt[i]);
	}
	if(!res) printf("Just a legend\n");
	else
	{
		for(int i=0;i<=res;i++) printf("%c",s[i]);
		puts("");
	}
}
int main()
{
	scanf("%s",s);
	len=strlen(s);
	pre_EKMP(s,len,nxt);

	return 0;
}

标签:nxt,typedef,int,char,字符串,include
From: https://www.cnblogs.com/viewoverlooking/p/17919894.html

相关文章

  • java中字符串的比较以及string 方法图解
    最近在项目中经常要用到字符串的比较,因此做了一个简略的总结,希望对大家有所帮助!!!!!!!!!!!!!!!1总体来说java中字符串的比较是比较引用,equals比较值的做法。(equals对于其他引用类型比较的是地址,这是因为object的equals方法比较的是引用),但是不同的声明方法字符串的比较结果也是不同的。例如:S......
  • 字符串转浮点型应用
    一、   工业应用中的问题1、     国内自定义协议众多,数值在计算机中存储方式五花八门。2、     计算机实际存储方式理解不容易或者忘记。3、     硬件技术发展,让使用存储内存不再是难解决问题,浪费存储内存和传输带宽。二、   计算机中浮点型简介1......
  • day 03-2 Python基础-字符串格式化
    2.字符串格式化字符串格式化,使用跟便捷的形式实现字符串的拼接。%format(推荐)f2.1%2.1.1基本格式化操作#%s是占位符,也成为字符串占位符#后面空格加%text="我叫%s,今年18岁"%"linzai"#:%前面加上一个空格print(text)name="linzai"text="我叫%s,今年18岁"......
  • 带逗号的字符串组装成List集合
    privateList<FileUrlDto>buildFileUrlMethod(StringfileUrl,StringfileName){List<String>files=newArrayList<>();List<String>fileNames=newArrayList<>();List<FileUrlDto>fileUrlDtoList=newArrayList&l......
  • C++ Qt开发:StringListModel字符串列表映射组件
    Qt是一个跨平台C++图形界面开发库,利用Qt可以快速开发跨平台窗体应用程序,在Qt中我们可以通过拖拽的方式将不同组件放到指定的位置,实现图形化开发极大的方便了开发效率,本章将重点介绍QStringListModel字符串映射组件的常用方法及灵活运用。QStringListModel是Qt中用于处理字符......
  • Python 把包含\\u4f20\\u5a92 unicode内容的字典字符串变成字典
    importjson#把包含\\u4f20\\u5a92unicode内容的字典字符串变成字典deftext_to_dict(text):dict1=json.loads(text)str_dict=str(dict1).replace('\\xa0','').replace('\'','"')dict_json=json.loads(s......
  • 代码随想录算法训练营第八天 | 344.反转字符串,541.反转字符串II,卡码网:54.替换数字,151.
    一、344.反转字符串题目链接:LeetCode344.反转字符串学习前:思路:相向指针。left=0,right=length-1,不停交换left和right的值时间复杂度:O(n)空间复杂度:O(1)学习后:了解swap函数通过位运算实现的方式二、541.反转字符串II题目链接:LeetCode541.反转字符串II学习前:思路:ne......
  • 处理字符串的常用函数(来自AI)
    当涉及到C语言的字符串处理时,有很多函数可以使用。以下是一些常见的字符串处理函数的列表,以及简短的描述:1.**strlen:**返回字符串的长度。```csize_tstrlen(constchar*str);```2.**strcpy:**将一个字符串复制到另一个字符串。```cchar*strcpy(char*d......
  • 字符串属性和方法
    一、什么叫字符串?String(字符串)数据类型表示零或多个16位Unicode字符系列二、字符串的声明?使用双引号("")、单引号(’’)或反引号(`)标示。三、字符串的属性和方法1.属性length使用length属性可以获取字符串的长度conststr='abcdefg'str.length//7字符串虽然有长度,但是......
  • Oracle 截取指定字符串
    --INSTR定位指定字符串--第一次出现的位置3(没有的话返回0)SELECTINSTR('某某/123/abc','/',1)FROMDUAL;--从第四个位置开始第一次出现的位置7SELECTINSTR('某某/123/abc','/',4,1)FROMDUAL;--从右向左第一个字符开始第一次出现的位置7SELECTINSTR('某某......