C语言代码随想录刷题计划 Day4

先附上地址方便对照着看:代码随想录

四、字符串

C语言中字符串通常是常量字符串或字符数组,可以用数组下标读取字符串的某一字符

常用库函数整理:

strlen(str):返回str字符串的字符长度,返回结果为 unsigned int

strcpy(str1,str2):将 str2 的复制到 str1 中去,注意不会检查 str1 是否够长,所以需要注意 str1 的长度是否能够容纳,str2 可以是字符数组也可以是字符串常量。

strncpy(str1,str2,n):将 str2 的前n个字符复制到 str1 ,会检查 str1 容量

strcmp(str1,str2):比较两个字符串的ASCII码大小,若出现不相同的字符,则以第一对不相同的字符比较结果为准,相等时返回0

strncmp(str1,str2,n):比较两个字符串前n个字符的ASCII码大小,返回值同上

strcat(str1,str2):将 str2 连接到 str1 的后面,结果保存在 str1 中,也要注意 str1 的长度

strstr(str1,str2):判断 str2 是否是 str1 的子串,如果是,返回 str2 在 str1 中首次出现的地址,否则返回NULL

strlwr(str):将 str 中的大写字母转为小写字母

strupr(str):将str中的小写字母转为大写字母

以上库函数都在string.h头文件中,dev c++可以ctrl加鼠标左键点击头文件进入,然后用ctrl + F搜索函数则可以看到函数需要传入的参数值,机试的时候很好用。

1、反转字符串:没啥好说的,直接莽就完了,注意字符串末尾是以 '\0' 结尾的,别把这个也反转了就行

void reverseString(char* s, int sSize) {
    char tmp;
    for(int i=0;i<sSize/2;i++){
        tmp = s[sSize-i-1];
        s[sSize-i-1] = s[i];
        s[i] = tmp;
    }
}

2、反转字符串II:思维有点僵化了,看到这种一段一段的处理应该就要想到改变for循环的变量处理,i++用多了确实会让人习惯啊。

基本思路:题目每2k个字符判断一次,那么在循环体里应该是 i+=(2*k),然后再根据题意判断情况,如果剩余字符少于k,那么反转右边界应该是 strlen(s)-1,否则就是 i+k-1

步骤如下:

step 1:设置外层for循环,注意 i+=(2*k)

step 2:设置反转两端,left 始终是 i ,但 right 需要根据剩余字符数是否小于 k 来判断

step 3:设置内部 while 循环用于反转字符串,反转思路同上一题,用 for 循环也可以,但是没有while 循环这么好的易读性

char* reverseStr(char* s, int k) {
    int left,right;
    for(int i=0;i<strlen(s);i+=(2*k)){ //step 1
        if(strlen(s)-i<k)  // step 2
            right = strlen(s)-1;
        else right = i+k-1;
        left = i; 
        while(left < right){ //step 3
            char tmp = s[left];
            s[left++] = s[right];
            s[right--] = tmp;
        }
    }
    return s;
}

3、替换数字:看完代码随想录的思路感觉不太适合机试,机试不需要这么多技巧,能够暴力就暴力,就算因为暴力解法扣掉一两个测试点的分也无伤大雅,考试最重要的就是用最短的时间拿到最多的分数,完全没必要追求极致的解法。

所以我选择暴力解法:创建一个足够大的新数组,遍历题目给的数组,遇到小写字母就直接存进去,遇见数字就用 strcat 函数拼上 number 字符串,我的机试建议是:能多用库函数就用库函数,不然机试的时候根本想不起来就麻烦了。注意结尾添一个 '\0' ,否则无法打印。

#include <stdio.h>
#include <string.h>

int main(){
    char s[10000];
    char res[100000];
    int cnt=0;
    scanf("%s",&s);
    for(int i=0;i<strlen(s);i++){
        if('a'<= s[i] && s[i] <= 'z')
            res[cnt++]=s[i];
        else if('0'<=s[i] && s[i]<='9'){
            strcat(res,"number");
            cnt += 6;
        }
    }
    res[cnt]='\0';
    printf("%s",res);
    return 0;
}

4、反转字符串中的单词:这道题很好,但不适合机试,因为机试采用的是输入输出的形式,所以用简单的 scanf 外面套一层循环其实就能去除字符串前后和中间的多余空格,然后存入一个二维字符串数组直接倒序输出,输出的时候每个单词中间输出一个空格就好。

但这道题还是很好的,可以学习一下如何处理多余空格。

理一下这道题的完整思路:首先去除多余空格,然后将整个字符串进行翻转,再依次翻转其中的每个单词。

去除多余空格步骤如下:

step 1:设置头指针和尾指针分别指向字符串开头和结尾,注意尾指针指向是 '\0'前面那个字符

step 2:分别移动头尾指针,找到第一个不是空格的字符,这一步其实就是去除两端多余空格

step 3:设定一个写入指针并从头指针所指位置开始遍历,直至尾指针,其中要注意去除中间多余空格。如何去除:注意到中间的多余空格一定是≥2个,所以当前字符如果是空格且下一个字符也是空格时就不予理会,直到当前是空格但下一个字符不是空格时再存入空格。

step 4:新写入的字符串尾部添加 '\0'

// 删除字符串两端和中间多余的空格
void removeExtraSpace(char* s) {
    int start = 0;  //step 1
    int end = strlen(s) - 1; 
    while (s[start] == ' ') start++; //step 2
    while (s[end] == ' ') end--;
    int slow = 0; // 指向新字符串的下一个写入位置的指针
    for (int i = start; i <= end; i++) { //step 3
        if (s[i] == ' ' && s[i+1] == ' ')  { // 如果当前字符是空格,并且下一个字符也是空格,则跳过
            continue;
        }
        s[slow] = s[i]; // 否则,将当前字符复制到新字符串的 slow 位置
        slow++; // 将 slow 指针向后移动
    }
    s[slow] = '\0'; //step 4
}

反转部分只要设置一个函数,传入待反转的字符串,反转起始位置和结束位置就行

// 翻转字符串中指定范围的字符
void reverse(char* s, int start, int end) {
    for (int i = start, j = end; i < j; i++, j--) {
        int tmp = s[i];
        s[i] = s[j];
        s[j] = tmp;
    }
}

最后实现题目要求的方法,注释很详细就不多赘述了

// 翻转字符串中的单词
char * reverseWords(char * s){
    removeExtraSpace(s); // 先删除字符串两端和中间的多余空格
    reverse(s, 0, strlen(s) - 1); // 翻转整个字符串
    int slow = 0; // 指向每个单词的开头位置的指针
    for (int i = 0; i <= strlen(s); i++) { // 遍历整个字符串
        if (s[i] ==' ' || s[i] == '\0') { // 如果当前字符是空格或空字符,说明一个单词结束了
            reverse(s, slow, i-1); // 翻转单词
            slow = i + 1; // 将 slow 指针指向下一个单词的开头位置
        }
    }
    return s; // 返回处理后的字符串
}

5、右旋字符串:

如果不限制空间复杂度的话,这道题我有两个思路:一个正儿八经的做一个新字符串,一个直接取巧输出

如果限制空间复杂度不能申请额外空间的话:就是代码随想录的思路和取巧输出

先看第一种:解法是先申请一个额外字符串,然后复制原本字符串的最后 n 个字符到新字符串的开头,然后利用 strcat 库函数将原来字符串的前 n 个拼接到新字符串后面,这种解法适合力扣这种需要返回值的刷题平台。

#include <stdio.h>
#include <string.h>

int main(){
    int num,i;
    char str[10000],res[10000];
    scanf("%d",&num);
    scanf("%s",&str);
    for(i=0;i<num;i++){
        res[i] = str[strlen(str)-num+i]; //将后n个字符放到新字符串开头
    }
    res[i]='\0'; 
    str[strlen(str)-num]='\0'; //注意截断原来的字符串再拼接
    strcat(res,str); //拼接
    printf("%s",res);
    
    return 0;
}

第二种就是直接取巧输出,先输出后n个字符,再输出前面 strlen(str)-n 个字符,适用于机试或一些给定输入让你输出的平台

#include <stdio.h>
#include <string.h>

int main(){
    int num,i;
    char str[10000];
    scanf("%d",&num);
    scanf("%s",&str);
    for(i=0;i<num;i++)
        printf("%c",str[strlen(str)-num+i]);
    for(i=0;i<strlen(str)-num;i++)
        printf("%c",str[i]);
    return 0;
}

如果限制空间复杂度为O(1)的话,那就等同于2010年的408算法题最优解

思路也很简单:先反转整个字符串,再分别反转前 n 个字符和后 strlen(str)-n 个字符

演示如下:设 n 为 2

abcdefg -> gfedcba -> fgedcba -> fgabcde,三次反转即可,由于C语言没有现成的反转函数,上一题中的reverse函数就可以拿来用

#include <stdio.h>
#include <string.h>

// 翻转字符串中指定范围的字符
void reverse(char* s, int start, int end) {
    for (int i = start, j = end; i < j; i++, j--) {
        int tmp = s[i];
        s[i] = s[j];
        s[j] = tmp;
    }
}

int main(){
    int num,i;
    char str[10000];
    scanf("%d",&num);
    scanf("%s",&str);
    reverse(str,0,strlen(str)-1);
    reverse(str,0,num-1);
    reverse(str,num,strlen(str)-1);
    printf("%s",str);
    return 0;
}

6、实现strStr():应付机试的话没必要手搓KMP算法,虽然它相对其他来说比较好写,也不需要暴力逐个字符进行比对,只需要用到库函数 strncmp 就好,代码非常简单,一看就会。

也是暴力的一种思路,但无需逐个字符比对,机试的时候多利用库函数总是没错的。

int strStr(char* haystack, char* needle) {
    int length=strlen(needle);
    for(int i=0;i<strlen(haystack);i++)
    {
        if(strncmp(haystack+i,needle,length)==0)
        return i;
    }
    return -1;
}

7、重复的子字符串:我的建议是直接暴力,尽量不要去手搓算法,这对C语言一点也不友好,对于机试的话你算法如果写错了调试都很麻烦。

bool repeatedSubstringPattern(char* s) {
    int n = strlen(s);
    for (int i = 1; i * 2 <= n; ++i) {
        if (n % i == 0) {
            bool match = true;
            for (int j = i; j < n; ++j) {
                if (s[j] != s[j - i]) {
                    match = false;
                    break;
                }
            }
            if (match) {
                return true;
            }
        }
    }
    return false;
}

总结:字符串类型的题目多用库函数,等到想学算法的再来手写算法也不迟。

  • 51
    点赞
  • 54
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值