Input Specification:
Each input file contains one test case, which gives D (in [0, 9]) and a positive integer N (≤ 40), separated by a space.
Output Specification:
Print in a line the Nth number in a look-and-say sequence of D.
Sample Input:
1 8
Sample Output:
1123123111
错误代码:
#include<iostream>
using namespace std;
int main(){
string d;
int n;
cin >> d >> n;
n--;
while(n--){
int num = 1;
char d2=d[0];
string ans="";
for (int i = 0; i < d.size();){
if(i+1<d.size()&&d[i+1]==d[i]){
num++;
i++;
}
if(i==d.size()-1||d[i+1]!=d[i]){
ans = ans + d2 + to_string(num);
num = 1;
d2 = d[++i];
}
}
d = ans;
}
printf("%s", d.c_str());
return 0;
}
AC代码:
#include<iostream>
using namespace std;
int main(){
string d;
int n,j;
cin >> d >> n;
n--;
//利用双指针i,j来找到连续相同字母的开头和结尾后一个,
//在这里,i的下一步循环取决于循环体中随i变化的j
while(n--){
string ans = "";
for (int i = 0; i < d.size();i=j){
for (j = i; j < d.size() && d[i] == d[j];j++);
ans += d[i] + to_string(j - i);
}
d = ans;
}
printf("%s", d.c_str());
return 0;
}
错误答案的思路:
1.我一开始的做法是先设立变量num和d2来存储依次相同的一个字母的数量和值,然后通过后一个char是否等于前一个char这个条件来求得每一次需要输出的结果(比如说111 则是13 ,3个1)。
但是,这个做法有2个繁杂得地方:
1.变量num和d2需要不断维护
2.当后一个char不等于前一个char的时候,也就是consecutive相同的字母已经都加好了的时候,判断条件有2个:A.后一个字母不等于前一个字母 B.i已经遍历到d的末尾了(因为如果不设立设个条件,那么当d=112的时候,末尾的2是不会被计入结果的!)
分析错误的原因:
结果很apparent,最后一个点超时了,没过,
当然了,自个是debug不出来的,看了别人的代码后发现: 可以用双指针来减少外层循环的次数!
可以看到,正确答案中的while中的逻辑:
while(n--){
string ans = "";
for (int i = 0; i < d.size();i=j){
for (j = i; j < d.size() && d[i] == d[j];j++);
ans += d[i] + to_string(j - i);
}
d = ans;
}
它这个i和j这两个指针的目的是为了标出连续的字母的开始和结尾的后一位,例如说:d = 112, 第一步循环 i = 0 , j = 2, 那么这一步的结果ans就有了:d[i] + j - i
首先可以观察到外层i循环的下一步是由j这个变量来决定的,也就是上面例子中j=2的时候,i的下一步就到了2开始,而循环体内for (j = i; j < d.size() && d[i] == d[j];j++); 的意思是找到第一个和d[i]不相同的索引j,这么做下来,i确实不一定要从头遍历到尾。
为了更加直观,上个图:
联想:
由此,突然想起曾经做过的一题:1071 Speech Patterns (25分)
其中的切割单词的问题,是不是也能用这个思路来实现?
于是就试了一下:
#include<iostream>
using namespace std;
bool judge(char c){
if((c>='0'&&c<='9')||(c>='a'&&c<='z')||(c>='A'&&c<='Z'))
return 1;
return 0;
}
int main(){
string x;
getline(cin, x);
int j;
for (int i = 0; i < x.size();i=j){
for (j = i; j < x.size() && judge(x[j]);j++);
cout << x.substr(i, j - i) << endl;
while(!judge(x[j])) j++;
}
return 0;
}
输入数据:
Can1: "Can a can can a can? It can!"
输出数据:
Can1
Can
a
can
can
a
can
It
can
完全可以!这么做,外层循环次数确实是减少的,内层中的while(!judge(x[j])) j++; 也只是重复做1步,损失是小于外层循环所需要做的步骤的!所以这么做是更加有效率,合理,简洁的!
最后附1071的完整代码:
#include<iostream>
#include<map>
using namespace std;
void toLower(string &x){
for (int i = 0; i < x.size();i++)
x[i] = tolower(x[i]);
}
bool judge(char c){
if((c>='0'&&c<='9')||(c>='a'&&c<='z')||(c>='A'&&c<='Z'))
return 1;
return 0;
}
int main(){
string x;
getline(cin, x);
toLower(x);
map<string, int> num;
int i,j;
for (i = 0; i < x.size() && !judge(x[i]);i++);
for (; i < x.size();i=j){
for (j = i; j < x.size() && judge(x[j]);j++);
num[x.substr(i,j - i)]++;
for ( ; j < x.size() && !judge(x[j]);j++);
}
string ans = "";
int maxTime = -1;
for (map<string,int>::iterator it = num.begin(); it != num.end();it++){
if(it->second>maxTime||(it->second==maxTime&&it->first<ans)){
maxTime = it->second;
ans = it->first;
}
}
cout << ans << " " << maxTime;
return 0;
}