寒假训练字符串专题

A - 雷同检测

题意很简单,按照其中短的字符串长度直接遍历判断就行。

#include<math.h>
#include<algorithm>
#include<stdio.h>
#include<iostream>
#include<set>
#include<map>
#include<string.h> 
using namespace std;
#define ll long long 
const int maxn=1e4+100;
int main()
{
	char s1[maxn],s2[maxn];
	gets(s1);
	gets(s2);
	int l1=strlen(s1);
	int l2=strlen(s2);
	l1=min(l1,l2);
	for(int i=0;i<l1;i++)
	{
		if(s1[i]==s2[i]) printf("%d ",i+1);
	}
	printf("\n");
	return 0;
}

B - 首字母大写

字符串基础,可以直接遍历判断

#include<math.h>
#include<algorithm>
#include<stdio.h>
#include<iostream>
#include<set>
#include<map>
#include<string.h> 
using namespace std;
#define ll long long 
const int maxn=1e4+100;
int main()
{
	char str[90];
	gets(str);
	int a=strlen(str);
	for(int i=0;i<a;i++)
	{
		if(i==0)
		{
			if(str[i]>=97&&str[i]<=122)
			str[0]-=32;
		}
		else 
		{
			if(str[i-1]==' '||str[i-1]=='\t'||str[i-1]=='\r'||str[i-1]=='\n')
			{
				if(str[i]>=97&&str[i]<=122)
				str[i]-=32;
			}
		}
	}
	puts(str);
	return 0;
}

C - 大小写转换

思路:直接遍历判断转换就行,注意是多实例输入。

#include<math.h>
#include<algorithm>
#include<stdio.h>
#include<iostream>
#include<set>
#include<map>
#include<string.h> 
using namespace std;
#define ll long long 
const int maxn=1e4+100;
int main()
{
	char s[maxn];
	while(scanf("%s", s) == 1)
	{
		int l=strlen(s);
		for(int i=0;i<l;i++){
			if(s[i]>='a'&&s[i]<='z'){
				s[i]-=32;
			}
		}
		printf("%s\n",s);
	}
	return 0;
}

D - 数字反转

本题数据范围1e9,可以直接用数写,不过用字符串更方便些直接从后往前遍历就行(就是特判下负数,如果为负数要先输出负号)

#include<math.h>
#include<algorithm>
#include<stdio.h>
#include<iostream>
#include<set>
#include<map>
#include<string.h> 
using namespace std;
#define ll long long 
const int maxn=1e4+100;
int main()
{
	char s[maxn];
	scanf("%s",s);
	int l=strlen(s);
	ll ans=0;
	if(s[0]=='-') printf("-");
	for(int i=l-1;i>=0;i--){
		if(s[i]!='-') ans=ans*10+(s[i]-'0');
	}
	printf("%d\n",ans);
	return 0;
}

E - 删除单词后缀

题意很简单就是删除3个特殊后缀,后缀长度最大为3很小直接判断后几位就行。

#include<math.h>
#include<algorithm>
#include<stdio.h>
#include<iostream>
#include<set>
#include<map>
#include<string.h> 
using namespace std;
#define ll long long 
const int maxn=1e4+100;
int main()
{
	char s[maxn];
	scanf("%s",s);
	int l=strlen(s);
	if(s[l-1]=='r'&&s[l-2]=='e') l-=2;
	else if(s[l-1]=='g'&&s[l-2]=='n'&&s[l-3]=='i') l-=3;
	else if(s[l-1]=='y'&&s[l-2]=='l') l-=2;
	for(int i=0;i<l;i++){
		printf("%c",s[i]);
	}
	printf("\n");
	return 0;
}

F - 判断字符串是否为回文

思路:顺读倒读都一样,那意思就是说字符串前后对称,这就好办了,看str[0]是否等于str[len-1],以此类推,如果不等于就标记然后break退出循环即可。

#include<bits/stdc++.h>
using namespace std;
string st1;
int main(){
	cin>>st1;
	int len  = st1.length();
	int j=len-1,flag=0;
	for(int i = 0;i < len;i++,j--){
		if(st1[i]!=st1[j]) {
			flag=1;
			break;
		}
	}
	if(!flag) cout<<"yes"<<endl;
	else cout<<"no"<<endl;
	return 0;
}

G - 基础数据结构——栈(1)

思路:利用栈的机制,第一步先入栈,如果栈底不空且匹配,之后.pop出栈,这样是合法的,如果不合法就flag然后break退出。

#include<string>
#include<map>
#include<stack>
#include<iostream>
#include<algorithm>
using namespace std;
int main(){
	string str;
	map<char,char> m;
	m['(']=')';
	m['[']=']';
	m['{']='}';
	while(getline(cin,str)){
		stack <char> vis;
		int flag=0;
		int len=str.size();
		for(int i = 0;i < len;i++){
			if(str[i]=='('||str[i]=='['||str[i]=='{'){
				vis.push(str[i]);
			}
			else if(str[i]==')'||str[i]==']'||str[i]=='}')
			{
				if(vis.size()&&m[vis.top()]==str[i]){
					vis.pop();
				}
				else { 
				flag=1;
				break;
				}
		}
	}
	if(!flag&&vis.size()==0){
		cout<<"yes"<<endl;
	}
	else cout<<"no"<<endl;
}
	return 0;
}


H - 字典序

判断字符串大小,可以直接调用strcmp函数判断。

#include<math.h>
#include<algorithm>
#include<stdio.h>
#include<iostream>
#include<set>
#include<map>
#include<string.h> 
using namespace std;
#define ll long long 
const int maxn=1e4+100;
int main()
{
	char s[maxn],a[maxn];
	scanf("%s",s);
	scanf("%s",a);
	if(strcmp(s,a)<0) printf("YES\n");
	else printf("NO\n");
	return 0;
}

I - 验证子串

字符串长度小于200,可以直接暴力判断s1(或s2)是否为子串。

#include <bits/stdc++.h>
#define ll long long
using namespace std;
const int maxn=1e5+10;
int main()
{
	string s1,s2;
	cin>>s1>>s2;
	int l1=s1.size();
	int l2=s2.size();
	if(l1<=l2){
		int i=0;
		int j=0,flag=0;
		while(j<l2&&i<l1){
			if(s2[j]==s1[i]) i++;
			j++;
			if(i==l1) flag=1;
		}
		if(flag) cout<<s1<<" is substring of "<<s2<<endl;
		else cout<<"No substring"<<endl;
	}
	else
	{
		int i=0;
		int j=0,flag=0;
		while(j<l1&&i<l2){
			if(s1[j]==s2[i]) i++;
			j++;
			if(i==l2) flag=1;
		}
		if(flag) cout<<s2<<" is substring of "<<s1<<endl;
		else cout<<"No substring"<<endl;
	}
	return 0;
}

J - 子串查找

题意:判断字符串B再A中出现了几次,A,B长度小于1e6。
思路:这是kmp算法的板子题,直接套用kmp算法求解。
关于kmp算法首先要知道next数组的含义,对模式串(也就是B)求next数组,知道next数组的含义就知道kmp匹配为什么快,其次kmp算法最难理解的地方就是如何求解next数组,详情参照下面两篇关于kmp的博客学习:详解kmp算法
从头到尾彻底理解kmp

#include<math.h>
#include<algorithm>
#include<stdio.h>
#include<iostream>
#include<set>
#include<map>
#include<string.h> 
#define ll long long 
using namespace std;
const int maxn=1e6+100;
char s[maxn],p[maxn];
int nex[maxn],lens,lenp,ans,n;
void getnex(int len)
{
	int i=0,j=-1;
	nex[0]=-1;
	while(i<len)
	{
		if(j==-1||p[i]==p[j])  
		{
			i++;j++;
			nex[i]=j;
		}
		else j=nex[j];
	}
}
void kmp()
{
	int i=0,j=0;
	while(i<lens)
	{
		if(j==-1||s[i]==p[j]) 
		{
			i++;j++;
		}
		else j=nex[j];
		if(j==lenp)
		{
			ans++;
			j=nex[j];
		}
	}
}
int main()
{
	scanf("%s %s",s,p);
	lens=strlen(s);
	lenp=strlen(p);
	getnex(lenp);
//	for(int i=0;i<=lenp;i++) printf("%d ",nex[i]);
//	printf("\n");
	kmp();
	cout<<ans<<endl;
	return 0;
}

K - 剪花布条

这道题也是kmp板题,跟上道题很像,唯一区别就是这道题先找到的子串直接就删除的,成功找到一个子串后再匹配模式串就要从头开始(j=0,而不是j=next[j]因为前面的已经被剪了)

#include<math.h>
#include<algorithm>
#include<stdio.h>
#include<iostream>
#include<set>
#include<map>
#include<string.h> 
using namespace std;
#define ll long long 
const int maxn=1e4+100;
char s[maxn],p[maxn];
int nex[maxn],lens,lenp,ans;
void getnex(int len)
{
	int i=0,j=-1;
	nex[0]=-1;
	while(i<len)
	{
		if(j==-1||p[i]==p[j])  
		{
			i++;j++;
			nex[i]=j;
		}
		else j=nex[j];
	}
}
void kmp()
{
	int i=0,j=0;
	while(i<lens)
	{
		if(j==-1||p[j]==s[i])
		{
			i++;j++;
		}
		else j=nex[j];
		if(j==lenp)
		{
			ans++;
			j=0;
		} 
	}
}
int main()
{
	while(~scanf("%s",s))
	{
		if(strlen(s)==1&&s[0]=='#') break;
		scanf("%s",p);
		ans=0;
		lens=strlen(s);
		lenp=strlen(p);
		getnex(lenp);
		kmp();
		printf("%d\n",ans);
	}
	return 0;
}

L - 最长回文子串

求解最长回文子串有好几种方法,其中Manacher算法是处理的最快的时间复杂度为·O (n)。
关于Manacher算法的原理可以参考下面的博客:
马拉车算法通俗教学

#include <iostream>  
#include <cstdio>  
#include <cstring>  
#define Min(a,b) a>b?b:a  
#define Max(a,b) a>b?a:b  
#define ll long long
using namespace std;  
const int maxn=1e5+10;
int Len[maxn];  
char str[maxn],s[maxn];  
int n,mx,id,len;  
void init()
{  
    int k=0;  
    str[k++] = '$';  
    for(int i=0;i<len;i++){  
        str[k++]='#';  
        str[k++]=s[i];  
    }  
    str[k++]='#';  
    len=k;  
}  
int Manacher()
{  
  Len[0] = 0;  
  int sum = 0;  
  mx = 0;  
  for(int i=1;i<len;i++)
  {  
    if(i < mx) Len[i] = Min(mx - i, Len[2 * id - i]);  
    else Len[i] = 1;  
    while(str[i - Len[i]]== str[i + Len[i]]) Len[i]++;  
    if(Len[i] + i > mx){  
      mx = Len[i] + i;  
      id = i;  
      sum = Max(sum, Len[i]);  
    }  
  }  
  return (sum - 1);  
}  
int main()  
{  
    memset(str,0,sizeof(str));
    scanf("%s",s);  
    len = strlen(s);  
    init();  
    int temp = Manacher();  
    printf("%d\n",temp);  

  return 0;  
}  

也可以动态规划来求解原理:
动态规划求解最长回文子串

#include<math.h>
#include<algorithm>
#include<stdio.h>
#include<iostream>
#include<set>
#include<map>
#include<string.h> 
using namespace std;
const int maxn=1e5+10;
char S[maxn];//A存序列,dp[i]存以i为结尾的连续序列的最大和
int dp[maxn][maxn];
int main()
{
	gets(S);//从下标为1开始读入
	int len=strlen(S),ans=1;
	memset(dp,0,sizeof(dp));
	for(int i=0;i<len;i++)
	{
		dp[i][i]=1;
		if(i<len-1)
		{
			if(S[i]==S[i+1])
				{
					dp[i][i+1]=1;
					ans=2;
				}
		}
	}
	//状态转移方程
	for(int L=3;L<=len;L++)//枚举子串长度
		for(int i=0;i+L-1<len;i++)//枚举子串起始端点 起始端点加上子串长度(子串长度包括他
		本身,所以要-1)必须小于总长,
			{
				int j=i+L-1;//子串右端点
				if(S[i]==S[j]&&dp[i+1][j-1]==1)
				{
					dp[i][j]=1;
					ans=L;//更新最长回文子串长度;
				}
			}
		cout<<ans<<endl;
		return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值