字符串相关

第一题(字节跳动)

王大锤的自动校对程序,总之就是定义了自动校对的模式如下:
1.三个字母连在一起,去掉一个,比如helllo->hello
2.两对一样的字母(AABB型)去掉第二对的一个字母,比如helloo->hello
3.上面规则优先从左到右匹配,如果是AABBCC,虽然AABB和BBCC都是错误拼写,但是优先考虑修复AABB,结果为AABCC
要求:
输入描述:第一行包括一个数字N,表示待校验字符串个数;
输出描述:N行,每行为修复后的字符串 

#include <iostream>
#include <string>
using namespace std;
 
string proofread(const char* s, int n)
{  
	  string ss;
      bool flag = false;
    //先遍历一遍去掉三个连续字母中的一个(规则一的筛选结果不影响规则二)  
	  for (int i = 0; i < n - 1; i++)  
	  {  
		  if (s[i] == s[i + 1])  
		  {  
			  if (!flag)  
			  {  
				  ss.push_back(s[i]);  //若两个相等,只保留一个(关键)
			      flag = true;  //已做改动
				  //i++;  
			  } 
		  }  
		  else  
		  {  
			  flag = false;  //未作改动
			  ss.push_back(s[i]); 
		  }  
	  }  
	  ss.push_back(s[n - 1]);  
	  //接着进行规则二的筛选  
	  int flag1 = -1;  
	  string result;  
	  for (int k = 0; k < ss.size() - 1; k++)  
	  {  
		  if (ss[k] == ss[k + 1])  
		  {  
			  if (flag1 == 1)  //若之前已经有一对字母相等
		       {  
			      flag1 = 2;  //现在又有一对字母相等
			      result.push_back(ss[k]);  //只保留一个字母
		       } 
		      else  
			  {  
				  flag1 = 1;  ////现阶段只有一对字母相等
				  result.push_back(ss[k]);  
			  }  
		  }  
		  else  
		  {  
			  if (flag1 != 2)  
				  result.push_back(ss[k]);  
			  if (k + 2 < ss.size() && ss[k + 1] != ss[k + 2])  
				  flag1 = -1;  
		  }  
	  } 
	  if (flag1 != 2)  
		  result.push_back(ss[ss.size() - 1]);    
	  return result;
}
 
int main()
{  
	int n;  
	cin >> n;  
	char* s = new char[n];  
	cin >> s;  
	if (n <= 2)  
		cout << "校正后的字符串为:" << s << endl;  
	else  
	{ 
		string ss = proofread(s, n);  
		cout << "校正后的字符串为:" << ss << endl; 
	}  
	return 0;
}
解2
#include<iostream>
#include<string>
using namespace std;

string check(char *str ,int n)
{
	string ss;
	string result;
	int flag=0;//表示没有字母相同
	int flags=0;
	for(int i= 0;i<n-1;i++)//helllo
	{
		if(str[i]==str[i+1]) 
		{
			flag++;//表示已有一对字母相同
			ss.push_back(str[i]);
			if(i + 2 < ss.size() && str[i+1]!=str[i+2]) 
			{
				flag =0;
				i++;
				ss.push_back(str[i]);
			 }
			else flag++;//连续两对重复,即三个重复
			if(flag == 2);
			{				
				i++;
				ss.push_back(str[i]);//输出第二个重复字母
				i++;//跳过第三个重复不作处理
			}
		}
		else ss.push_back(str[i]);
	}
	ss.push_back(str[n-1]);
	for(int k= 0;k<ss.size()-1;k++)//hllelloo --> hllello
	{
		if(ss[k]==ss[k+1]) 
		{
			flags++;//表示已有一对字母相同
			result.push_back(ss[k]);
			k++;
			if (k + 2 < ss.size() && ss[k + 1] != ss[k + 2])  //关键(没有连续相等)
				  flags = 0; 
			if(flags == 2) continue;
			else result.push_back(ss[k]);
		}
		else result.push_back(ss[k]);
	}
	//if (flags != 2)  //补充
	result.push_back(ss[ss.size()-1]);

	return result;
}

int main(){
	int n;
	cin>>n;	
	char* str = new char[n];  
	cin >> str;  
	if(n<=2) cout<<str<<endl;
	else cout<<check(str,n)<<endl;
	return 0;
} 

第二题:letcode(最长回文子串)

解法1:动态规划  时间复杂度:O(N2)

DP

动态规划的方法,我会在下一篇单独来介绍,这里只说明此题的DP代码

 对于字符串str,假设dp[i,j]=1表示str[i...j]是回文子串,那个必定存在dp[i+1,j-1]=1。这样最长回文子串就能分解成一系列子问题,可以利用动态规划求解了。首先构造状态转移方程

上面的状态转移方程表示,当str[i]=str[j]时,如果str[i+1...j-1]是回文串,则str[i...j]也是回文串;如果str[i+1...j-1]不是回文串,则str[i...j]不是回文串。

      初始状态

  • dp[i][i]=1
  • dp[i][i+1]=1 if str[i]==str[i+1]

      上式的意义是单个字符,两个相同字符都是回文串。

解法2:暴力法 复杂度太高,O(N^3)

#include<iostream>
#include<string>
#include<vector>
using namespace std;

//方法1:动态规划dp
string is(string str)
{
	int len = str.size();
	if (len == 0)return 0;
	if (len == 1)return str;
	int start=0;
	int longest =1;
	vector<vector<int>> dp(len,vector<int>(len));//定义了一个vector容器,元素类型为vector<int>,初始化为包含len个vector<int>对象,每个对象都是一个新创立的vector<int>对象的拷贝,而这个新创立的vector<int>对象被初始化为包含len个0。
	for(int i=0;i<len;i++)
	{
		dp[i][i] =1;
		if(i<len-1)
		{
			if(str[i]==str[i+1])
			{
				dp[i][i+1] =1;
				start = i;
				longest =2;
			}
		}
	}
	for(int length=3;length<=len;length++)//子串长度
	{
		for(int i=0;i+length-1<len;i++)
		{
			int j = i+length-1;
			if(str[i]==str[j]&&dp[i+1][i-1] ==1)
			{
				dp[i][j] =1;
				start = i;
				longest =length;
			}
		}
	}
	return str.substr(start,longest);
}

 //方法2:暴力法   
   
	string longestPalindrome(string s) {
		int len = s.size();
		int start=0,maxlength=1;//记录最大回文子串的起始位置以及长度
		for(int i =0;i<len;i++)
		{
			for(int j=i+1;j<len;j++)				
			{	int temp1 ,temp2;
				for(temp1 = i,temp2 = j;temp1 <len && temp2<len && temp1<temp2;temp1++,temp2--)
				{
					if(s[temp1]!=s[temp2])
					break;
				}
				if(temp1>=temp2 && j-i+1> maxlength)
				{
					start = i;
					maxlength = j-i+1;
				}
				
			}
		}   
        return s.substr(start,maxlength);
    }

int main(){

	string str;  
	cin >> str;  
	cout<<longestPalindrome(str)<<endl;
	//system("pause");
	return 0;
} 

/*网易雷火笔试-打印机(区间dp)
有一台神奇的打印机,可打印的字母范围大写A-Z,每次只能从纸带上的任意位置开始打印同一字母任意次数,并且可以覆盖之前同一位置上已经打印上的字母。给定一个目标字符串,问最少需要打印多少次才能打印出给定字符串。例如,目标为ABCBA,先打印AAAAA,再打印BBB,再打印C,所以答案为3。

分析:没什么难的,看到有划水过去的,但是答案是错的,有求联通块的,不理解是怎么建的图

我是用的区间dp,dp[i][j]表示打印【i,j】需要的打印数,[l,r]最差的选择是在[l,j-1]的基础上在末尾单独的添加上那一个字符,所以dp[i][j]最大为dp[i][j-1]+1

那么dp[i][j]=min(dp[i][t]+d[t+1][j-1]),但是要注意会产生非法访问(即i>j)dp的只有在类似"AA"这样的子串的时候才会发生,将dp非法访问的都置为1就好了,或者这样情况手动处理

有的人思路认为如果找到一个t<j使得s[t]==s[j],那么dp[i][j]=dp[i][t]+{(t+1)-(j-1)之间的不同字符数},但是ADBDBA这样的样例是过不去的
*/
 

#include<iostream>
#include<vector>
#include<string>
using namespace std;

int solve(string s)
{
	int len = s.size();
	vector<vector<int>>dp(len,vector<int>(len));
	for(int i=0;i<len;i++)
	{
		dp[i][i]=1;//区间为i i(1)的必定只要打印一次(初始设置)
	}
	for(int k=2;k<=len;k++)//区间长度>=2
	{
		for(int i=0;i<=len-k;i++)//错点:没有等号(?
		{
				int j=i+k-1;//?
				dp[i][j]=dp[i][j-1]+1;//最差情况下
				for(int t=j-1;t>=i;t--)
				{
					if(s[t]==s[j])//?
					dp[i][j]=min(dp[i][t]+dp[t+1][j-1],dp[i][j]);
				}
		}
	
	}
	return dp[0][len-1];
}
int main(){
	string s;    
 
    getline(cin,s);
    cout<<solve(s)<<endl;
    return 0;
}

猜你喜欢

转载自blog.csdn.net/starluqifei/article/details/88697573