算法題-去除重複字母

去除重複字母(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

相關文章
相關標籤/搜索