字符串 KMP及NEXT应用

  • 把字符串中某字符串换为其他字符

getline(cin,s)可以输入带空格的整行字符串。

getline(cin,line,'#');终止条件是输入“#”,you are the best #one 读入为you are the best

两个字符串相差2,所以引入t,记录相差多少,每次起点处减掉。

#include<iostream>
#include<cstdio>
#include<cmath>
#include<algorithm>
#include<cstring>
#include<string>
using namespace std;
typedef long long ll;
int main()
{
	string s,s1,s2,ans;
	s1="marshtomp";
	s2="fjxmlhx";
	int j;
	while(getline(cin,s)&&s[0]!='\0')//输入整行
	{
		ans=s;j=0;
		for(int i=0;i<ans.length();i++)
		{
			ans[i]=tolower(ans[i]);
		}
		int node=0,t=0;
		while(node<ans.length())
		{
			j=ans.find(s1,node);//字符串,起始位置
			if(j==-1) break;
			else
	                {
	        	s.replace(j-2*t,9,s2);t++;//更换起点,长度,更换为字符串
	        	node=j+9;
			}
		}
		cout<<s<<endl;
	}
	return 0;
 } 
  • 把一串字符 给定长度的前缀和后缀 修改为相同的

把需要相同的字符分为一组,每组中都改为众数最大的那个字符。

#include<iostream>
#include<cstdio>
#include<cmath>
#include<algorithm>
#include<cstring>
#include<string>
#include<map>
using namespace std;
typedef long long ll;
int main()
{
	int t,k,count,l,mx,cishu;
	string s;
	scanf("%d",&t);
	while(t--)
	{
		cin>>s;count=0;
		scanf("%d",&k);
		int len=s.length();
		if(len>=k*2)
		{
			for(int i=0;i<k;i++)
			{
				if(s[i]!=s[i+len-k])  count++;
			}
		}
		else
		{
			for(int i=0;i<len-k;i++)
			{
				map<char,int> m;
				l=i;mx=0;cishu=0;
				while(l<len)
				{
					m[s[l]]++; mx=max(mx,m[s[l]]); l=l+len-k;cishu++;
				}
				count=count+cishu-mx;
			}
		}
		printf("%d\n",count);
	}
	return 0;
 } 
  • 给你两个由小写英文字母组成字符串s,t。如果s[i...i+|t|-1] = t (|t|表示t的长度),则认为s[i...i+|t|-1] 是t的相等串。现在有q次询问,每次询问你s[L..R]中有多少个不同位置的t的相等串。

用数组a[ ]记录该位置后多少位形成一个字符串t,初始化所有位置,最后判断时尾部减掉t的长度

#include<iostream>
#include<cstdio>
#include<cmath>
#include<algorithm>
#include<cstring>
#include<string>
using namespace std;
typedef long long ll;
ll a[100005];
ll n,m,q,c,b,l;
string s,t;
void fun()
{
    int j;
    for(int i=0;i<=n-m;i++)
    {
    	if(s[i]==t[0])
    	{
    		for(j=1;j<m;j++)
                {
                if(s[i+j]!=t[j])    break;
                }
		if(j==m)     a[i]=1;
	}
    }
}
int main()
{
	ll count;
	scanf("%lld%lld%lld",&n,&m,&q);
	cin>>s;
	cin>>t;
	memset(a,0,sizeof(a));
	fun();
	while(q--)
	{
		scanf("%lld%lld",&c,&b);
		count=0;
		for(int i=c-1;i<=b-m;i++)
		{
			if(a[i])  count++;
		}
		printf("%lld\n",count);
	} 
	return 0;
 } 
  • KMP寻找子串首次出现的位置

#include<iostream>
#include<cstdio>
#include<cmath>
#include<algorithm>
#include<cstring>
#include<string>
using namespace std;
typedef long long ll;
int a[1000005];
int b[10005];
int ne[10005];
int m,n;
void getnext()
{
	int i=0,j=-1;
	ne[0]=-1;
	while(i<m)
	{
		if(j==-1||b[i]==b[j])
		{
			i++;j++;ne[i]=j;
		}
		else j=ne[j];
	}
}
int index()
{
	int i=0,j=0;
	getnext();
	while(i<n&&j<m)
	{
		if(j==-1||a[i]==b[j])
		{
			i++;j++;
		}
		else j=ne[j];
	}
	if(j==m)  return i-m+1;
	else      return -1;
}
int main()
{
	int t;
	scanf("%d",&t);
	while(t--)
	{
		scanf("%d%d",&n,&m);
		for(int i=0;i<n;i++)    scanf("%d",&a[i]);
		for(int i=0;i<m;i++)    scanf("%d",&b[i]);
		printf("%d\n",index());
	}
	return 0;
}
  • 模式串在待匹配字符串中出现次数

#include<iostream>
#include<cstdio>
#include<cmath>
#include<algorithm>
#include<cstring>
#include<string>
using namespace std;
const int maxn=1000010;
char a[10010],b[maxn];
int ne[10010];
int mu,zi,t;
void getnext()
{
	ne[0]=-1;
	int i=0,j=-1;
	while(i<zi)
	{
		if(j==-1||b[i]==b[j])
		{
			i++;j++;ne[i]=j; 
		}
		else j=ne[j];
	}
}
int count()
{
        getnext();
	int ct=0,j=0,i=0;
	while(i<mu)
	{
		while(j&&a[j]!=b[i])
		{
			j=ne[j];
		}
		if(a[j]==b[i])  j++; 
		if(j==zi)
		{
			ct++;j=ne[j];
		}
		i++;
	}
	return ct;
}
int main()
{
	scanf("%d",&t);
	while(t--)
	{
	        scanf("%s%s",&a,&b);
		zi=strlen(a);mu=strlen(b);
		printf("%d\n",count());
	}
	return 0;
}
  • 求字符串中相同前后缀的长度

       字符串s的前缀是指,从s的开始字符到s的任意字符为止的子串。字符串s的后缀是指,从s的任意字符到s的最后字符为止的子串。s是既是自身的前缀也是自身的后缀。

  这个问题利用KMP算法的next[]数组来解。首先对于输入的字符串s,计算其next[]数组。然后,根据next[]数组的值进行进一步的计算。对于字符串s的第i个字符s[i],next[i]定义为字符s[i]前面最多有多少个连续的字符和字符串s从初始位置开始的字符匹配。

  从后到前匹配前缀和后缀。如果前缀与后缀匹配,则下一个前缀与后缀匹配的字符串只能在前缀中。

#include<cstdio>
#include<string>
#include<algorithm>
#include<cstring>
#include<iostream>
using namespace std;
typedef long long ll;
const int maxn=400005;
int ne[maxn],a[maxn];
int len;
string s;
void getnext()
{
    int i=0,j=-1;
    ne[0]=-1;
    while(i<len)
    {
        if(j==-1||s[i]==s[j])
        {
            i++;j++;
            ne[i]=j;
         }
        else j=ne[j];
    }
}
int main()
{
    while(cin>>s&&s!="NULL")
    {
        len=s.size();
        getnext();
        int t=ne[len-1],cnt=0;
        while(t!=-1)
        {
            if(s[t]==s[len-1]) a[cnt++]=t+1;
            t=ne[t];
        }
        for(int i=cnt-1;i>=0;i--)
            printf("%d ",a[i]);
        printf("%d\n",len);
    }
    return 0;
}
  • 找循环节,至少添加多少个字符形成循环字符

len-next[ len ]为最小循环节!!!

#include<iostream>
#include<cstdio>
#include<cmath>
#include<algorithm>
#include<cstring>
#include<string>
using namespace std;
typedef long long ll;
const int maxn=1e5+10;
char a[maxn];
int  ne[maxn];
int t,len,zu;
void getnext()
{
	int i=0,j=-1;
	ne[0]=-1;
	while(i<=len)
	{
		if(j==-1||a[i]==a[j])
		{
			i++;j++;ne[i]=j;
		}
		else j=ne[j];
	}
}
int main()
{
	scanf("%d",&t);
	getchar();
	while(t--)
	{
		scanf("%s",&a);
		len=strlen(a);
		getnext();
		zu=len-ne[len];
		if(len==zu)   printf("%d\n",len);
		else if(len%zu==0)    printf("0\n");
		else      printf("%d\n",zu-len%zu);
	}
	return 0;
}
  • 给出一个字符串S,求其所有前缀中,字符长度与出现次数的乘积的最大值。

对每一个前缀出现的次数都叠加到其包含的前缀当中去,即状态转移方程 d[Next[i]] += d[i] ( d[i]代表长度为 i 的前缀出现的次数)

#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cstring>
#include<string>
using namespace std;
typedef long long ll;
const int maxn=100005;
ll ne[maxn];
ll num[maxn];
char s[maxn];
ll len;
void get_next()
{
    ll i=0,j=-1;
    ne[0]=-1;
    while(i<len)
    {
        if(j==-1||s[i]==s[j])
        {
            i++;j++;
            ne[i]=j;
        }
        else j=ne[j];
    }
}
int main()
{
    scanf("%s",&s);
    len = strlen(s);
    get_next();
    ll mx=0;
	memset(num,0,sizeof(num));
    for(ll i=len;i>0;i--)
    {
	num[i]++;
        num[ne[i]]+=num[i];
        mx=max(mx,num[i]*i);
    }
    printf("%lld\n",mx);	
    return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值