字节跳动笔试题——万万没想到之聪明的编辑

转载请注明链接,有问题请及时联系博主:Alliswell_WP

题目描述:

时间限制:C/C++ 1秒,其他语言2秒
空间限制:C/C++ 32M,其他语言64M


我叫王大锤,是一家出版社的编辑。我负责校对投稿来的英文稿件,这份工作非常烦人,因为每天都要去修正无数的拼写错误。但是,优秀的人总能在平凡的工作中发现真理。我发现一个发现拼写错误的捷径:

1. 三个同样的字母连在一起,一定是拼写错误,去掉一个的就好啦:比如 helllo -> hello
2. 两对一样的字母(AABB型)连在一起,一定是拼写错误,去掉第二对的一个字母就好啦:比如 helloo -> hello
3. 上面的规则优先“从左到右”匹配,即如果是AABBCC,虽然AABB和BBCC都是错误拼写,应该优先考虑修复AABB,结果为AABCC

我特喵是个天才!我在蓝翔学过挖掘机和程序设计,按照这个原理写了一个自动校对器,工作效率从此起飞。用不了多久,我就会出任CEO,当上董事长,迎娶白富美,走上人生巅峰,想想都有点小激动呢!
……
万万没想到,我被开除了,临走时老板对我说: “做人做事要兢兢业业、勤勤恳恳、本本分分,人要是行,干一行行一行。一行行行行行;要是不行,干一行不行一行,一行不行行行不行。” 我现在整个人红红火火恍恍惚惚的……


请听题:请实现大锤的自动校对程序

输入描述:
第一行包括一个数字N,表示本次用例包括多少个待校验的字符串。
后面跟随N行,每行为一个待校验的字符串。

输出描述:
N行,每行包括一个被修复后的字符串。

输入例子1:
2
helloo
wooooooow

输出例子1:
hello
woow

>vi main.c

#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
#include<string>
#include <algorithm>

using namespace std;

// 初始化,获取数据
int  getFileContent(char *pFileName/*in*/, char ***p, int *nLine)
{
    cout << "请输入待检测的行数N: " << endl;
    cin >> *nLine;
    cout << "请依次输入待检测的数据: " << endl;
    getchar();
    char **tmpP = NULL;
    tmpP = (char **)malloc(sizeof(char *) * *nLine);
    if (tmpP == NULL)
    {
        cout << "func getFileContent() err: " << endl;
        return -1;
    }

    // 依次输入n行内容
    for (int i = 0; i < *nLine; i++)
    {
        tmpP[i] = (char *)malloc(20);
        cin.getline(tmpP[i], 20);
    }

    //间接赋值
    *p = tmpP;  
    return 0;
}

int  getFileContent_Free(char **p, int iLine)
{
    int i = 0;
    if (p == NULL)
    {
        return 0;
    }

    for (i = 0; i < iLine; i++)
    {
        free(p[i]);
    }

    free(p);
    //p = NULL;

    return 0;
}


// 既可以把指针所指向的内存空间给释放掉 并且把实参重新赋值成NULL
int  getFileContent_Free2(char ***p, int iLine)
{
    getFileContent_Free(*p, iLine);
    *p = NULL;
    return 0;
}

// 依次检测每行
int checkFileContent(char **p, int iLine)
{
    if ((p == NULL)&&(iLine == 0))
    {
        return -1;
    }
    for (int i = 0; i < iLine; i++)
    {
        string sString = p[i];
        transform(sString.begin(), sString.end(), sString.begin(), ::tolower);
        string sonString;
        // 规则:检测小写字母
        for (int j = 0; j < 26; j++)
        {
            char ch = (char)(j + 'a');
            sonString.clear();
            // 规则1:检测3个相邻的字母
            sonString.append(3, ch);
            int pos = sString.find(sonString);
            // 找到
            while (pos != -1)
            {
                sString.erase(pos, 1);
                pos = sString.find(sonString, pos);
                strcpy(p[i], sString.c_str());
            }
            // 规则2:检测aabb相邻的字母
            sonString.clear();
            sonString.append(2, ch);
            for (int k = 0; k < 26; k++)
            {
                char ch2 = (char)(k + 'a');
                sonString.erase(2, 2);
                sonString.append(2, ch2);
                int pos2 = sString.find(sonString);
                // 找到
                while (pos2 != -1)
                {
                    sString.erase(pos2+2, 1);
                    pos2 = sString.find(sonString, pos2);
                    strcpy(p[i], sString.c_str());
                    break;
                }
            }
        }
    }


    return 0;
}

// 依次打印每行
void printFileContend(char **p, int iLine)
{
    for (int i = 0; i < iLine; i++)
    {
        cout << p[i] << endl;
    }
}


int main()
{
    int        ret = 0, i = 0;
    char    *myFileName = NULL;
    char    **myP = NULL;
    int        myLine = 0;

    cout << "程序开始..." << endl;
    //获取文件内容
    ret = getFileContent(myFileName, &myP, &myLine);
    if (ret != 0)
    {
        cout << "func getFileContent() err" << endl;
        return ret;
    }

    // 依次检测每行
    ret = checkFileContent(myP, myLine);
    if (ret != 0)
    {
        cout << "func getFileContent() err" << endl;
        return ret;
    }

    //打印文件内容 按照行
    printFileContend(myP, myLine);

    getFileContent_Free2(&myP, myLine);

    system("pause");
    return 0;
}

 

编程问题总结:

1)动态分配内存?如何输入n行字符串?

思路:用数组先整体分配空间(存储结点),然后为每行的每个变量再分配空间,指向每行字符串的首地址的位置。

注意:释放内存的时候,顺序相反,先释放每行字符串的首地址指向的空间,后释放存储结点。


C++的istream中的类(如cin)提供了一些面向行的类成员函数:getline()和get()。这两个函数都读取一行输入,直到到达换行符。然而,随后getline()将丢弃换行符,而get()将换行符保留在输入队列中。
(1)getline()
getline()函数读取整行,它使用通过回车键输入的换行符来确定输入结尾。要调用这种方法,可以使用cin.getline()。该函数有两个参数。第一个参数是用来存储输入行的数组名称,第二个参数是要读取的字符数。如果这个参数为20,则函数最多读取19个字符,余下的空间用于存储自动在结尾处添加的空字符。getline()成员函数在读取指定数目的字符或遇到换行符时停止读取。

例如:假设要使用getline()将姓名读入到一个包含20个元素的name数组中,可以用这样的函数调用:
cin.getline(name,20);
这将一行读入到name数组中——如果这行包含的字符不超过19个。
getline()函数每次读取一行。它通过换行符来确定行尾,但不保存换行符。相反,在存储字符串时,它用空字符来替换换行符。


(2)get()
istream类有另一个名为get()的成员函数,该函数有几种变体。其中一种变体的工作方式与getline()类似,它们接受的参数相同,解释参数的方式也相同,并且都读取到行尾。但get()并不再读取并丢弃换行符,而是将其留在输入队列中。假设连续两次调用get():

    cin.get(name,ArSize);
    cin.get(name,ArSize);    //a problem

由于第一次调用后,换行符将留在输入队列中,因此第二次调用时看到的第一个 字符便是换行符。因此get()认为已经到达行尾,而没有发现任何可读取的内容。如果不借助于帮助,get()将不能跨过该换行符。
get()有另一种变体。使用不带任何参数的cin.get()调用可读取下一个字符(即使是换行符),因此可以用它来处理换行符,为读取下一行输入做准备:

    cin.get(name,ArSize);   //read first line
    cin.get();              //read newline
    cin.get(name,ArSize);   //read second line

get()相比于getline()可以使得输入更仔细。例如,假设用get()将一行读入数组。如何知道停止原因是由于已经读取了整行,而不是由于数组已经填满了呢?查看下一个字符,如果是换行符,说明已经读取了整行;否则,说明该行中还有其他输入。

getline()使用起来简单一些,但get()使得检查错误更简单些。


 

2)函数设计,如何使代码分块更好处理?

    // 获取数据内容
    getFileContent();

    // 依次检测每行
    checkFileContent();

    //打印文件内容 按照行
    printFileContend();
    
    // 释放内存
    getFileContent_Free();

3)C/C++如何整行读入字符串?

方法一:scanf()读入char[]

使用方法:

char str[1024];
scanf("%[^\n]",&str);
getchar();

说明:在scanf函数中,可以使用%c来读取一个字符,使用%s读取一个字符串, 但是读取字符串时不忽略空格,读字符串时忽略开始的空格,并且读到空格为止,因此只能读取一个单词,而不是整行字符串。

其实scanf函数也可完成这样的功能,而且还更强大。这里主要介绍一个参数,%[ ],这个参数的意义是读入一个字符集合。[ ]是个集合的标志,因此%[ ]特指读入此集合所限定的那些字符,比如%[A-Z]是输入大写字母,一旦遇到不在此集合的字符便停止。如果集合的第一个字符是"^",这说明读取不在"^"后面集合的字符,既遇到"^"后面集合的字符便停止。注意此时读入的字符串是可以含有空格的,而且会把开头的空格也读进来。

注意:如果要循环的多次从屏幕上读取一行的话,就要在读取一行后,在用%c读取一个字符,将输入缓冲区中的换行符给读出来。否则的话,在下一次读取一行的时候,第一个就遇到'\n',匹配不成功就直接返回了。这里可以用scanf()或者getchar()函数读取换行符。

方法二:getchar()读入char[]

使用方法:

char str[1024];
int i=0;
while((str[i]=getchar())!='\n')
    i++;
getchar();

说明:这样一个一个读也可以,也会把开头的空格读进来。最后也需要考虑换行符,使用getchar()读出来。

方法三:gets()读入char[]

使用方法:

char str[1024];
gets(str);

说明:感觉这个就是多个getchar的集合函数,很好用。功能是从标准输入键盘上读入一个完整的行(从标准输入读,一直读到遇到换行符),把读到的内容存入括号中指定的字符数组里,并用空字符'\0'取代行尾的换行符'\n'。读入时不需要考虑换行符。

方法四:getline()读入string或char[]

使用方法:

string str;
getline(cin,str);//读入string

char str2[1024];
cin.getline(str2,1024);//读入char数组

说明:这是比较常用的方法,cin.getline第三个参数表示间隔符,默认为换行符'\n'。读入不需要考虑最后的换行符。

方法五:get()读入char[]

使用方法:

char str3[1024];
cin.get(str3,1024);//读入char数组

说明:get函数读入时需要考虑最后的换行符,也就是说,如果用get读入多行数据,要把'\n'另外读出来,一般使用cin.get(str,1024).get();来读入多组数据。

参考——https://www.cnblogs.com/AlvinZH/p/6798023.html

4)每次输入3后,但只能输入两行数据?

思路:读取了之前在缓冲区中的换行符(“\n”),所以读取之前先getchar();把缓冲区中的数据清空。

5)如何实现依次检测每行,实现规则1,实现查找相同3个字母的字符串?

思路:先实现子串“aaa”的子串在字符串的查找,然后实现字符‘a’到‘z’的遍历,拼接完成子串的过程,然后遍历在(每行的)字符串中查找。

6)c++ 如何获取输出26个字母?

#include <iostream>
using namespace std;
void main()
{
int i;
cout<<"输出小写字母:";
for (i=0;i<26;i++)
cout << (char) (i+'a'); //小写
cout << endl;
cout<<"输出大写字母:";
for (i=0;i<26;i++)
cout << (char) (i+'A'); // 大写
cout << endl;
}

7)如何实现依次检测每行,实现规则2,实现查找AABBCC得到AABCC?

思路:先实现子串“aaa”的子串在字符串的查找,然后实现字符‘a’到‘z’的遍历,拼接完成子串的过程,然后遍历在(每行的)字符串中查找。

8)如果输入的字符串中有大小写,大小写字母如何处理?

思路:由于大写字母AAa或AABBcc,这样的存在,为避免出错,所以统一使用C++自带的算法(transform(s.begin(), s.end(), s.begin(), ::tolower);),转为小写。

注意:添加头文件#include<algorithm>

9)c++ strcpy放到程序中报错?

思路:这段代码放到VS2015没有问题,但是在自测程序报错,查了资料,添加头文件#include<cstring>

 

 

 

转载请注明链接,有问题请及时联系博主:Alliswell_WP

posted on 2020-08-12 11:44  Alliswell_WP  阅读(427)  评论(0编辑  收藏  举报

导航