1081. 不同字符的最小子序列

算法记录

LeetCode 题目:

  返回 s 字典序最小的子序列,该子序列包含 s 的所有不同字符,且只包含一次。



说明

一、题目

  s 由小写英文字母组成

二、分析

  • 如果没有最小字典序这个约束条件的话就直接进行一个去重输出即可,但是偏偏有这么一个条件在里面。
  • 最小字典序指的就是排在前面的字符的字典序要相对较小,那不就回归到一个单调性的特点上来了。
  • 维护一个单调栈用来记录字典序,一个数组来记录字符出现的次数,一个集合来判断当前的字符有没有已经放入到栈中。
class Solution {
    public String smallestSubsequence(String s) {
        Deque<Character> queue = new LinkedList();
        int[] map = new int[26];
        Set<Character> set = new HashSet();
        for(char c : s.toCharArray()) map[c - 'a']++;
        for(char c : s.toCharArray()) {
            if(!set.contains(c)) {
                while(!queue.isEmpty() && 
                        map[queue.peekLast() - 'a'] > 0 &&
                        c < queue.peekLast())
                    set.remove(queue.pollLast());  
                queue.add(c);
                set.add(c);  
            }
            map[c - 'a']--;
        }
        StringBuilder builder = new StringBuilder();
        while(!queue.isEmpty()) builder.append(queue.pollFirst());
        return builder.toString();
    }
}

总结

熟悉单调栈的使用方法以及字符的处理。

实现词法分析需要先了解词法分析的概念。词法分析是编译器中的一个重要步骤,它负责将源代码中的字符序列转化为有意义的单词流(Token Stream),并对每个单词赋予一个类型码(Token Type)。在实现词法分析时,需要遵循以下步骤: 1. 定义Token类型:在编写词法分析器之前,需要先定义Token类型,并为每个Token类型赋予一个类型码。 2. 读入源代码:编写词法分析器需要先读入源代码,将源代码保存在一个字符数组中。 3. 分析源代码:遍历源代码中的每个字符,将字符序列转化为Token序列,并为每个Token赋予一个类型码。 4. 输出Token序列:将Token序列输出,供后续的语法分析器使用。 在实现词法分析器时,可以采用有限状态自动机(Finite State Automaton)的方式来实现。有限状态自动机是一种抽象的计算模型,可以用来描述词法分析器的运行过程。其基本思想是:将字符序列视为一个有向图,其中每个节点表示一个状态,每条边表示一个字符的转移。在词法分析器中,有限状态自动机可以用来识别Token类型,将字符序列转化为Token序列。可以采用状态转移表来实现有限状态自动机,也可以采用状态转移图来表示有限状态自动机。 下面是一个简单的以C语言小子集实现词法分析的示例代码: ``` #include <stdio.h> #include <ctype.h> typedef enum { TOKEN_INT, TOKEN_FLOAT, TOKEN_PLUS, TOKEN_MINUS, TOKEN_STAR, TOKEN_SLASH, TOKEN_LPAREN, TOKEN_RPAREN, TOKEN_EOF } TokenType; typedef struct { TokenType type; union { int intValue; float floatValue; } value; } Token; Token getNextToken(char *input) { static char *p = input; Token token; while (isspace(*p)) { p++; } switch (*p) { case '+': token.type = TOKEN_PLUS; token.value.intValue = 0; p++; break; case '-': token.type = TOKEN_MINUS; token.value.intValue = 0; p++; break; case '*': token.type = TOKEN_STAR; token.value.intValue = 0; p++; break; case '/': token.type = TOKEN_SLASH; token.value.intValue = 0; p++; break; case '(': token.type = TOKEN_LPAREN; token.value.intValue = 0; p++; break; case ')': token.type = TOKEN_RPAREN; token.value.intValue = 0; p++; break; case EOF: token.type = TOKEN_EOF; token.value.intValue = 0; break; default: if (isdigit(*p)) { token.type = TOKEN_INT; token.value.intValue = 0; while (isdigit(*p)) { token.value.intValue = token.value.intValue * 10 + (*p - '0'); p++; } if (*p == '.') { token.type = TOKEN_FLOAT; token.value.floatValue = token.value.intValue; p++; float decimal = 0.1; while (isdigit(*p)) { token.value.floatValue += (*p - '0') * decimal; decimal /= 10; p++; } } } else { printf("Invalid character: %c\n", *p); token.type = TOKEN_EOF; token.value.intValue = 0; p++; } break; } return token; } int main() { char input[] = "1 + 2 * (3 - 4) / 5.0"; Token token; do { token = getNextToken(input); switch (token.type) { case TOKEN_INT: printf("INT: %d\n", token.value.intValue); break; case TOKEN_FLOAT: printf("FLOAT: %f\n", token.value.floatValue); break; case TOKEN_PLUS: printf("PLUS\n"); break; case TOKEN_MINUS: printf("MINUS\n"); break; case TOKEN_STAR: printf("STAR\n"); break; case TOKEN_SLASH: printf("SLASH\n"); break; case TOKEN_LPAREN: printf("LPAREN\n"); break; case TOKEN_RPAREN: printf("RPAREN\n"); break; case TOKEN_EOF: printf("EOF\n"); break; default: break; } } while (token.type != TOKEN_EOF); return 0; } ``` 在这个示例代码中,我们定义了9种Token类型,分别为:整数、浮点数、加号、减号、乘号、除号、左括号、右括号和EOF。其中,整数和浮点数是具有值的Token类型,因此在Token结构体中定义了一个value联合体,用于存储Token的值。在getNextToken函数中,我们使用一个静态指针p来遍历输入的字符序列,然后根据当前字符的类型识别Token类型,并将Token返回。最后,在主函数中,我们遍历Token序列,并根据Token类型输出相应的信息。 需要注意的是,这个示例代码只是一个简单的实现,没有考虑代码的健壮性和可扩展性。在实际的编译器中,需要对词法分析器进行更加细致和严谨的设计和实现。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值