第一题(字节跳动)
王大锤的自动校对程序,总之就是定义了自动校对的模式如下:
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;
}