字符串

14. 最长公共前缀

char * longestCommonPrefix(char ** strs, int strsSize){//是公共前缀,意思是不去看中间的子串,我还在动态规划解决,傻傻傻
    if(strsSize==0)
        return "";
    char *p=strs[0];//定义一个指针变量指向第一个字符串
    int i,j;
    for(i=1;i<strsSize;i++){
        j=0;
        while(p[j]!='\0'&&strs[i][j]!='\0'){
            if(p[j]!=strs[i][j])
                break;
            j++;
        }
        p[j]='\0';
        if(p==NULL)//是所有字符串的公共前缀,若任一两者出现不相等的情况,则返回空
            return "";
    }
    return p;
}

3. 无重复字符的最长子串

与这题类似,我之前在哪遇到过一次,但是拿到题,还是想到的暴力法,多加总结呀。不过好在能非常快速看懂最优解

int lengthOfLongestSubstring(char * s){//思维是有的,但是想到的是暴力破解,并没有想到用下标记录数字上一次出现的位置来代表是否出现过
    int i, j = 0, count = 0, max = 0, start = 0;
    int *index=(int *)malloc(sizeof(int)*128);//ascall码,用一字节表示字符,最高位不用,所以最多128个字符。
    memset(index,0,sizeof(int)*128);//index[s[i]]表示该字符还未出现过
    for(i=0;s[i]!='\0';i++)     
    {
        if(index[s[i]]>start)//若if成立,代表s[i]在0~(i-1 )之间至少出现过一次
        {                       
            count = i-start;
            if(count>max)
            {
                max = count;
            }
            start = index[s[i]];//start移动到重复数字上一回出现位置的下一位
        }
        index[s[i]] = i+1;//记录s[i]出现的最新位置;这个+1很巧哦。虽然本意是为了和0区别,因为直0表示没有出现过嘛。但是很巧的是,避免了遇到重复数字时的挪动
    }
    count = i-start;
    free(index);
    return count>max?count:max;
}

242. 有效的字母异位词

题目意思是判断s和t是否是相同字母不同排序结果的字符串 因为这道题有small tip,限制了只是小写字母哟 第二版相对于第一版来说,就是空间复杂度少了一些,区别在while代码块哪。第一版分别对字符串s和t各个字母出现的次数进行加和;第 二版则是字符串s在对应的位置+,字符串t在对应的位置-。

由于本题的字符串只包含 26 个小写字符,因此可以使用长度为 26 的整型数组,来映射字符与出现次数,然后比较两个字符串出现的字符数量是否相同

第一版

bool isAnagram(char * s, char * t){
    int length1=strlen(s),length2=strlen(t),i;
    int *count1=(int *)malloc(sizeof(int)*26);
    memset(count1,0,sizeof(int)*26);
    int *count2=(int *)malloc(sizeof(int)*26);
    memset(count2,0,sizeof(int)*26);
    bool temp=true;
    if(length1!=length2)
        return false;
    while(i<length1){
        count1[s[i]-'a']++;
        count2[t[i]-'a']++;
        i++;
    }
    for(i=0;i<26;i++){
        if(count1[i]!=count2[i]){
            temp=false;
            break;
        }  
    }
    free(count1);
    free(count2);
    return temp;
}

第二版

bool isAnagram(char * s, char * t){
    int length1=strlen(s),length2=strlen(t),i;
    int *count1=(int *)malloc(sizeof(int)*26);
    memset(count1,0,sizeof(int)*26);
    bool temp=true;
    if(length1!=length2)
        return false;
    while(i<length1){
        count1[s[i]-'a']++;//区别在此
        count1[t[i]-'a']--;
        i++;
    }
    for(i=0;i<26;i++){
        if(count1[i]!=0){//区别在此
            temp=false;
            break;
        }  
    }
    free(count1);
    return temp;
}

796. 旋转字符串

哇,这道题说难也不难,我写了三个版本,顺序为倒序,依次排放在下面了,每个说明的序号和代码的序号都一一对应。

  1. 第三版时间复杂度O(n),空间复杂度O(n)。最坑的是strncpy最后不会自动填充’\0’!!!然后我查了半天才找到为什么越界的原因,emmmm。不过喃strcpy就会自动填充哦。
  2. 第二个空间复杂度O(1),思维可以用线性代数矩阵的逆来理解,是一个意思。其实第二版很有意思的,这算法是我在算法视频上看到的,my dream universty had tested it at 2019.
  3. 我不想说我最开始写的那个版本,完全和旋转的概念没有搭上边,只解了字符串中没有重复字母的情况,通过31/49的用例。
bool rotateString(char * A, char * B){
    

 1. 

    int length=strlen(A),i,length1=strlen(B);
    if(!length&&!length1)
        return true;
    char *result=(char *)malloc(sizeof(char)*(length+1));
    for(i=0;i<length;i++){
        strncpy(result,A+i,length-i);
        strncpy(result+length-i,A,i);
        result[length]='\0';
        if(!strcmp(result,B)){
            return true;
        }    
    }
    return false;
}

 2. 

    void ReverseString(char *s,int from,int to){
    char t;
    while(from<to){
        t=s[from];
        s[from]=s[to];
        s[to]=t;
        from++;
        to--;
    }
}
void leftRotateString(char*s,int n,int m){//可以用线性代数,矩阵的逆来理解。字符串的长度为n,旋转m次
    ReverseString(s,0,m-1);
    ReverseString(s,m,n-1);
    ReverseString(s,0,n-1);
}
bool rotateString(char str[], char * B){//说明一下这个地方,之前我用的指针接收字符串A,但是旋转一次之后字符串就被改了,我第二次旋转还需要用原来的字符串。
    int i,length=strlen(str),length1=strlen(B);
    if(length==0&&length1==0)
        return true;
    char temp[length+1];
    strcpy(temp,str);//这个就用一个数组接收
    for(i=1;i<=length;i++){
            //char* p=A;
            strcpy(str,temp);//每一次循环,都把我保存的字符串副本给str
           leftRotateString(str,length,i);
           if(!strcmp(str,B))
                return true;
    }
    return false;
}
 3. 

    /*int left,right,i,length=strlen(B),decimal1,decimal2=(int)A[0],length1=strlen(A);只解了字符串中没有重复字母的情况,通过31/49的用例
    if(length!=length1)
        return false;
    char *left1=(char *)malloc(sizeof(char)*(length+1));
    char *left2=(char *)malloc(sizeof(char)*(length+1));
    char *right1=(char *)malloc(sizeof(char)*(length+1));
    char *right2=(char *)malloc(sizeof(char)*(length+1));
    bool flag=false;
    for(i=0;i<length;i++){
        decimal1=(int)B[i];//c语言单个字符不能进行比较,必须转换为ASCALL码
        if(decimal1==decimal2){
            left=i;
            break;
        }     
    }
    right=length-left;
    strncpy(left1,A,right);
    strncpy(right1,A+right,left);
    strncpy(left2,B,left);
    strncpy(right2,B+left,right);//给字符串赋值之后要加结束符号‘\0’,不然越界
    left1[right]='\0';
    left2[left]='\0';
    right1[left]='\0';
    right2[right]='\0';
    if(!strcmp(left1,right2)&&!strcmp(left2,right1))
        flag=true;
    free(left1);
    free(left2);
    free(right1);
    free(right2);
    return flag;*/

1332. 删除回文子序列

这道题真是绝了,读题很重要呀!!! 然后本题考的是子序列,emmmmm
注释掉的部分是我按照子串的思路写的,通过测试用例大概31/49,在字符串“baabb”的时候,如果按照回文子串来讲,应该删除3次,但是我的是一次,方法有问题。
总之,在看完别人说的坑之后,觉得自己费的时间太可惜了。

int removePalindromeSub(char * s){//空字符串0次;整体回文1次;第三种则是一次子序列a,一次子序列b,所以最多2次
    int length=strlen(s);
    int i=0,j=length-1;
    if(length==0)
        return 0;
    while(i<j){
        if(s[i++]!=s[j--]) 
        return 2;
    } 
    return 1;
    /*int i,j,length=strlen(s),m,sum=0;
    bool flag;
    for(i=0;i<=length/2;i++){
        m=i;
        flag=true;
        for(j=length-1;j>i;j--){
            if(s[m]!=s[j])
                flag=false;
                break;
            m++;
        }
        if(flag){
            sum=length+i-j;
            break;
        }
            
    }
    if(s==NULL){
        return 0;
    }else{
        return sum;
    }*/
}

1108. IP 地址无效化

用到一个库函数memcpy()

char * defangIPaddr(char * address){
    int length=strlen(address),count=0,sum=length+7,i;
    char *returnArray=(char *)malloc(sizeof(char)*(sum));//7为三对[]和一个结束符号
    memset(returnArray,'0',sizeof(char)*(sum));
    returnArray[length+6]='\0';
    for(i=0;i<length;i++){
        if((*address)!='.'){
            *returnArray++=*address++;
        }else{
            memcpy(returnArray,"[.]",3);
            returnArray+=3;
            address++;
        }
    } 
    return returnArray-length-6;//最后returnArray挪到队尾了,需要前移
}

409. 最长回文串

被注释掉的部分的错误原因是,我没有成双成对的思想。想的是把最大的奇数找到,然后利用它。结果没想到如果最大的奇数是5,那我奇数3对应的字符其中的两也可以做回文字符阿。蠢的不行了。
诺,下图为被注释掉的代码的运行结果。
在这里插入图片描述

int length=strlen(s),i,max=0,sum=0;
    int *count1=(int *)malloc(sizeof(int)*52);
    memset(count1,0,sizeof(int)*52);
    for(i=0;i<length;i++){
        if((s[i]-'A')>=0&&(s[i]-'A')<=25)
            count1[s[i]-'A']++;
        else
            count1[s[i]-'A'-6]++;
    }
    for(i=0;i<52;i++){
       sum+=count1[i]/2*2;//这样就可以成双成对了,不用再去寻找最大的奇数是多少
    }
    if(sum<length)//说明至少有一个为单个的,无法和别人配对
        sum++;
    free(count1);
    return sum;

205. 同构字符串

*解题思路

最开始看到这题,我想的是,先用两数组把两字符串里各字符出现的次数记录下来,然后按从大到小的顺序排,然后再比较是否相等。理论上这方法是可行的,不过我没去试。
然后换了大神的思维,这个方法我用笔画了画才懂的,有点晦涩。记录一个字符上次出现的位置,如果两个字符串中的字符上次出现的位置一样,那么就属于同构。

bool isIsomorphic(char * s, char * t){//开辟两个256的整型数组,计算每个字母出现的位置
    int *preindex1=(int *)malloc(sizeof(int)*256);
    int *preindex2=(int *)malloc(sizeof(int)*256);
    memset(preindex1,0,sizeof(int)*256);//0代表从未出现过
    memset(preindex2,0,sizeof(int)*256);
    int i,length=strlen(s);
    for(i=0;i<length;i++){
        if(preindex1[s[i]]!=preindex2[t[i]])
            return false;
        preindex1[s[i]]=i+1;//最新的s[i]出现在哪个位置
        preindex2[t[i]]=i+1;
    }
    free(preindex1);
    free(preindex2);
    return true;
}

1170. 比较字符串最小字母出现频次

这道题没啥说的,对于我自己而言加深了对二级指针和二维数组的异同的理解
char ** queries,queries[i]代表代表第i+1行数据

int f(char *p){//思考着思考着,就忘记了还有f函数判断最小字符这要求
     int i,min,length=strlen(p),count=0;
     min=p[0];
     for(i=1;i<length;i++){
        if(p[i]<min)
            min=p[i];
     }
     for(i=0;i<length;i++){
         if(p[i]==min)
            count++;
     }
     return count;
 }
int* numSmallerByFrequency(char ** queries, int queriesSize, char ** words, int wordsSize, int* returnSize){
    int *count1=(int *)malloc(sizeof(int)*queriesSize);
    int *count2=(int *)malloc(sizeof(int)*wordsSize);
    int *result=(int *)malloc(sizeof(int)*queriesSize);
    memset(result,0,sizeof(int)*queriesSize);
    int i,j,k,length;
    for(i=0;i<queriesSize;i++){
        count1[i]=f(queries[i]);
    }
    for(j=0;j<wordsSize;j++){
        count2[j]=f(words[j]);
    }
    for(i=0;i<queriesSize;i++){
        j=0;
        while(j<wordsSize){
            if(count1[i]<count2[j])
                result[i]++;
            j++;
        }
        
    }
    free(count1);
    free(count2);
   (*returnSize)=queriesSize;
    return result;
}

67. 二进制求和

第二遍看自己写的,觉得真的……,zz

首先我是考虑到了,两个加数的位数不同,但第一想法是判断哪个字符数组先遍历完。不过,碰到一个问题,假设数组a先遍历完,我第一次是直接把未遍历完的数组b直接赋值给结果数组C的,忽略了a[0]+b[j]的和也有进位的可能。
So, i change my thought. 不足位的补0。i>-1?a[i]-‘0’:0)+(j>-1?b[j]-‘0’:0,这一句就是实现我的思想的

char * addBinary(char * a, char * b){
    int length1=strlen(a),length2=strlen(b),length;//length1的长度未包含'\0'
    length=length1>length2?(length1+1):(length2+1);//加一,以防最高位进位

    char *c=(char *)malloc(sizeof(char)*(length+1));
    memset(c,'0',sizeof(char)*(length+1));//再+1,最后一位为结束符
    c[length]='\0';//字符数组的最后一位下标为length

    int *carry=(int *)malloc(sizeof(int)*(length+1));//用于存放进位位的
    memset(carry,0,sizeof(int)*(length+1));
    int i,j,k,sum;
    char temp;
    for(i=length1-1,j=length2-1,k=0;((i>=0)||(j>=0)||carry[k])&&k>-1;i--,j--,k++){//char数组的最后一位为\0
        sum=(i>-1?a[i]-'0':0)+(j>-1?b[j]-'0':0);//两个二进制数,位数不同,用0补齐
        if((sum+carry[k])>1){
            c[k]=(sum+carry[k])-2+'0';
            carry[k+1]=1;
        }else{
            c[k]=sum+carry[k]+'0';
        }
    }
    for(i=0,j=length-1;i<j;i++,j--){//由于上一个for中,我的k老是有问题,越界,不得不把结果逆序存放,然后再倒序一下,有点费时间
        temp=c[i];
        c[i]=c[j];
        c[j]=temp;
    }
    if(c[0]=='0'){//也即最高位没有进位
        for(i=0;i<length;i++){
            c[i]=c[i+1];
        }
    }
    free(carry);
    return c;
}

696. 计数二进制子串

  1. 循环生成偶数位的字符串。然后再用变量i,j前后遍历。i与j不同的次数只能为一次,如果是两次及以上则不符合拉。以01或者10为中间分界,左边i,j指向的数相同的次数需要和右边i,j指向的数相同的次数
    相同。aaaaa,没有实现出来。
    以下就是大佬的代码了哟,总体思路和我很像阿,我们计数的方式不一样,这就是区别吧。
    好好记得这个思路过程。

用静态图表述一下思维,等我会画动图了,我再更新
在这里插入图片描述

int countBinarySubstrings(char * s){//有想过最i,j以前以后走,也知道要记录01or10的变化,但在通知数量,记录值方面还是没有想的那么好
    int i,pre=0,cur=1,count=0;
    for(i=0;i<strlen(s)-1;i++){
        if(s[i]==s[i+1])
            cur++;
        else{
            pre=cur;
            cur=1;
        }
        if(pre>=cur)
            count++;
    }
    return count;
}
  1. 最后一个单词的长度
int lengthOfLastWord(char * s){
    int i=0,j=0,length=0;
    while(s[j]){
        if(s[j]==' '&&s[j+1]&&s[j+1]!=' ')//记得处理最后1bit为空的情况,还有空格连续的情况
            i=j+1;
        j++;
    }
    while(s[i]&&s[i]!=' '){
        i++;
        length++;
    }
    return length;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值