算法题-去除重复字母

去除重复字母(LeetCode-困难)

给你一个仅包含小写字母的字符串,请你去除字符串中重复的字母,使得每一个字母只出现一次。需保证返回的结果字典序最小(要求不能打乱其余字符的相对位置)bcacb => acb

思路(递归)

例如:cfebfacb算法

  1. 准备工做,把每一个字符出现的次数记录在一个数组中
  2. 先找到一个预处理字符的位置,从左到右遍历,没遍历一个字符,把记录数组对应的出现的次数-1,当次数== 0时,这个字符的位置就是预处理字符的位置 =》第一次找到的是e,预处理字符的位置就是e的位置
  3. 预处理字符 前面的字符中若是有小于它的,记录小于它的字符位置。=》c小于e的,预处理 = c的位置,获取c之后的字符串。若是没有小于e的,预处理 = e的位置
  4. 以预处理点 如今就变成处理点,以处理点为分割点,获取后面的子串,并把子串中全部和预处理点相等的都干掉。
  5. 返回:strcat(处理点字符 + 把子串交给步骤0),以子串字符=='\0‘做为递归出口

经过上面的思路走一遍例子:cfebfacb 处理点+子串数组

  1. c + febfab
  2. e + bacb
  3. a + b
  4. b + '\0'

结果:ceabmarkdown

代码

char* delDuplicateLetters(char *string) {
    //1.记录字母出现的次数
    int arr[26] = {0};
    for (int i = 0; i < strlen(string); i++) {
        arr[string[i] - 'a'] ++;
    }
    
    //2.遍历循环字符串中的字符,每找到一个字符,对应的字母出现次数的数组中记录的出现次数--,当出现的次数减到0,说明后面不会再出现,顺便也记录一下当前遍历到的元素中,最小的元素的位置
    int pos = 0;
    for (int i = 0; i < strlen(string); i++) {
        if (string[i] < string[pos]) {
            pos = i;
        }
        if (--arr[string[i] - 'a'] == 0) {
            break;
        }
    }
    
    //3.获取有消失字符以前的最小字符的位置
    char *t = (char*)malloc(sizeof(char));
    *t = string[pos];
    
    //4.根据最小字符的位置为分割点,截取后面子字符串
    char subString[strlen(&string[pos + 1])];
    //5.删除子串中,全部的上面循环记录中的最小元素
    strcpy(subString, &string[pos + 1]);
    int j = 0;
    for (int i = 0; subString[i] != '\0'; i++) {
        if (subString[i] != string[pos]) {
            subString[j++] = subString[i];
        }
    }
    subString[j]='\0';
    
    //6.递归和出口
    return (strlen(string) == 0) ? "" : strcat(t, delDuplicateLetters(subString));
}

int main(int argc, const char * argv[]) {
    // insert code here...
    printf("Hello, World!\n");
    
    char *str = delDuplicateLetters("dbcbcab");
    printf("%s\n", str);
    
    return 0;
}
复制代码

运行

===我是分割线===

思路

使用栈的方式实现-参考代码中的注释数据结构

代码

代码中的栈操做,请参考文章 数据结构-栈代码实现post

char* stringDelChar(char *string, char chr) {
    char *ret = (char*)malloc(sizeof(char) * strlen(string));
    int j = 0;
    for (int i = 0; i < strlen(ret); i++) {
        if (chr != ret[i]) {
            ret[j++] = ret[i];
        }
    }
    ret[j] = '\0';
    return ret;
}

char* delDuplicateLetters(char *string) {
    //1.字符串中的字符最后一次出现的位置
    int lastPos[26] = {0};
    for (int i = 0; i < strlen(string); i++) {
        lastPos[string[i] - 'a'] = i;
    }
    //2.初始化 栈
    SqStack stack;
    initStack(&stack);
    //3.建立一个字符串,里面存放已经入栈的字符,至关于一个容器
    char *set = (char*)malloc(sizeof(char) * strlen(string));
    //4.开始遍历
    for (int i = 0; i < strlen(string); i++) {
        char c = string[i];
        //5.当前字符是否已经在容器中
        char *isHas = strchr(set, c);
        if (!isHas) {
            SElemType topElem;
            getTopElem(stack, &topElem);
            //6.栈不为空 当前字符小于栈顶字符 该字符不是最后一次出现
            while (!isEmptyStack(stack) && c < *topElem && lastPos[c] > i) {
                //7.出栈,出容器
                SElemType temp;
                popElemFromStack(&stack, &temp);
                set = stringDelChar(set, *temp);
            }
            //8.入栈,入容器
            strcat(set, &c);
            SElemType temp = (SElemType)malloc(sizeof(char));
            *temp = '\0';
            strcpy(temp, &c);
            pushElem2Stack(&stack, temp);
        }
    }
    
    //遍历栈,新的字符串
    char *ret = (char*)malloc(sizeof(stack.top));
    for (int i = 0; i <= stack.top; i++) {
        SElemType c = stack.data[i];
        strcat(ret, c);
    }
    
    free(set);
    return ret;
}

int main(int argc, const char * argv[]) {
    // insert code here...
    printf("Hello, World!\n");
    
    char *str = delDuplicateLetters("cbacdcbc");
    printf("%s\n", str);
    return 0;
}
复制代码

运行

更简洁的代码

最近学习到更加简洁的代码,总体思路和上面的栈实现同样,我的认为比个人写法更简洁的点是对c语言更透彻一些。直接上代码,代码中有我我的的注释。学习

char *removeDuplicateLetters(char *s) {
    if (strlen(s) <= 1) {
        return s;
    }
    //字符出现次数数组
    char record[26] = {0};
    //初始化栈
    int len = (int)strlen(s);
    char *stack = (char*)malloc(sizeof(char) * 2 * len);
    memset(stack, 0, sizeof(char) * 2 * len);
    int top = -1;
    
    //循环遍历存入字符出现的次数
    for (int i = 0; i < len; i++) {
        record[s[i]-'a']++;
    }
    //循环遍历字符串中的字符
    for (int i = 0; i < len; i++) {
        //字符是否存在栈中
        int isExist = 0;
        for (int j = 0; j <= top; j++) {
            if (s[i] == stack[j]) {
                isExist = 1;
                break;
            }
        }
        
        if (isExist == 1) {
            //字符存在栈中,把记录数组相关的记录--
            record[s[i] - 'a']--;
        } else {
            //开始循环遍历,条件:栈不为空 && 栈顶元素大于当前字符 && 记录数组中当前字符的记录大于1(也就是后面还会有这个字符出现)
            while (top > -1 && stack[top] > s[i] && record[stack[top]-'a'] > 1) {
                //字符记录--
                record[stack[top]-'a']--;
                //出栈
                top--;
            }
            //入栈当前字符
            stack[++top] = s[i];
        }
    }
    //最后给字符加入结束点
    stack[++top] = '\0';
    
    return stack;
}
int main(int argc, const char * argv[]) {
    // insert code here...
    printf("Hello, 去除重复字符-栈!\n");
    
    char *str = delDuplicateLetters("cbacdcbc");
    printf("%s\n", str);
    
    char *str1 = removeDuplicateLetters("cbacdcbc");
    printf("%s\n", str1);
    return 0;
}
复制代码

传送门

算法题-查找子串第一次出现和KMP算法学习spa

相关文章
相关标签/搜索