1140 Look-and-say Sequence (20分) 解决测试点4超时 详解

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;
}
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值