Leetcode 初级算法 - 字符串

版权声明:Shared With Do What The Fuck You Want To Public License https://blog.csdn.net/nekonull/article/details/86505866

Leetcode 初级算法 - 字符串

原始地址:https://github.com/jerrylususu/leetcode-easy
二级标题格式:[章节内题号] [题库内题号] [题目标题]

3 387 字符串中的第一个唯一字符

我的思路:

用了两个数组 一个存储首次出现时间 一个存储总共出现次数

最后把这两个数组同时扫一遍 以此确定第一个唯一字符

(题目中已经说明了只有小写字母会涉及)

其他思路:

直接用 HashMap 存储(字母,次数) 先扫完一次字符串 然后从头开始检查出现次数

或者用 indexOf 和 lastIndexOf 得到字符出现的位置 检测是否重合

public int firstUniqChar(String s) {
    int len = s.length();
    int[] first = new int[26];
    int[] time = new int[26];
    for (int i = 0; i < len; i++) {
        int pos = s.charAt(i)-97; // a->97 in ascii
        if(time[pos]==0){
            first[pos]=i+1;
            time[pos]=1;
        } else {
            time[pos]++;
        }
    }
    int minpos = Integer.MAX_VALUE;
    for (int i = 0; i < 26; i++) {
        if(first[i]<minpos&&time[i]==1){
            minpos=first[i];
        }
    }
    if(minpos==Integer.MAX_VALUE){
        return -1;
    } else {
        return minpos-1;
    }
}

4 242 有效的字母异位词

异位词:两个单词由相同的字母 但是不同的顺序组成 则互为异位词

问题:一定要求字母的出现次数相同吗?(根据测试 似乎是的)

我的思路:先判定长度是否相等 再转成 char array 然后直接 char array 排序比较 (写起来方便一点…)

其他思路:用数组记录每个字母出现的次数 然后比较(复杂度上从O(nlogn)降到了O(n))

public boolean isAnagram(String s, String t) {
    if(s.length()!=t.length()){
        return false;
    }
    char[] a1 = s.toCharArray();
    char[] a2 = t.toCharArray();
    Arrays.sort(a1);
    Arrays.sort(a2);
    for (int i = 0; i < a1.length; i++) {
        if(a1[i]!=a2[i]){
            return false;
        }
    }
    return true;
}

5 125 验证回文字符串

我的思路:示例看起来不太友好 大小写混合 还有不参与的字符

因此先把字符串转成小写 然后分别从头部和尾部往回比对 遇到不参与比较的字符跳过

其他思路:看到个正则表达式的… 直接用[^0-9a-zA-Z]+来判定合法字符

还有的用StringBuffer翻转 然后直接用equals方法比较

public static boolean isPalindrome(String s) {
    int i=0,j=s.length()-1;
    while(i<j){
        while (!isValidChar(s,i)&&i<j) i++;
        while (!isValidChar(s,j)&&i<j) j--;
        System.out.println(i+" "+j);
        System.out.println(s.charAt(i)+" "+s.charAt(j));
        if(i<j){
            if(s.charAt(i)!=s.charAt(j)){
                return false;
            } else {
                i++;j--;
            }
        }
    }
    return true;
}

public static boolean isValidChar(String s, int pos){
    int a = s.charAt(pos);
    return ((48<=a&&a<=57)||(65<=a&&a<=90)||(97<=a&&a<=122));
}

6 8 字符串转换整数 (atoi)

题目本身写的很详细了 按照这个步骤写就好了

我的思路:跳过开头字符->判断正负号->串联数字->Integer.parseInt->处理异常

被卡的坑:-42 (最后忘了把正负号加回去), +1 (只处理了负号), +-2 (???), " +0 123" (应该对是否开始parse进行判断)

最后用了三个boolean来表示状态:是否正数 是否有正负号 是否正在parse

其他思路:我这里有点偷懒 用的是自带的解析 应该按照个位十位一层一层加上去 (类似于RK算法那样每次乘10 就不用判断是第几位了) 同时边加 边处理溢出 (处理溢出:与MAX_VALUE进行比较)来源

public static int myAtoi(String str) {
    StringBuilder buffer = new StringBuilder();
    int len = str.length();
    boolean isPositive = true, marked = false, doing = false;
    for (int i = 0; i < len; i++) {
        char c = str.charAt(i);
        if(c==' '&&!doing){
            continue;
        }
        if(c=='+'){
            if(!marked){
                marked = true;
                doing = true;
            } else
                break;
        } else if(c=='-') {
            if(!marked) {
                isPositive = false;
                marked = true;
                doing = true;
            }else
                break;
        }else if(48<=c&&c<=57){
            if(!marked){
                marked = true;
            }
            buffer.append(c);
            doing = true;
        }else {
            break;
        }
    }
    int res = 0;
    if(buffer.length()==0){
        return 0;
    }
    try{
        res = Integer.parseInt(buffer.toString());
        res = isPositive?res:-res;
    } catch (NumberFormatException e){
        res = isPositive?Integer.MAX_VALUE:Integer.MIN_VALUE;
    }
    return res;
}
// source: https://leetcode.com/problems/string-to-integer-atoi/discuss/4643/Java-Solution-with-4-steps-explanations

public int myAtoi(String str) {
    int index = 0, sign = 1, total = 0;
    //1. Empty string
    if(str.length() == 0 || str.trim().length() == 0) return 0;

    //2. Remove Spaces
    while(str.charAt(index) == ' ' && index < str.length())
        index ++;

    //3. Handle signs
    if(str.charAt(index) == '+' || str.charAt(index) == '-'){
        sign = str.charAt(index) == '+' ? 1 : -1;
        index ++;
    }
    
    //4. Convert number and avoid overflow
    while(index < str.length()){
        int digit = str.charAt(index) - '0';
        if(digit < 0 || digit > 9) break;

        //check if total will be overflow after 10 times and add digit
        if(Integer.MAX_VALUE/10 < total || Integer.MAX_VALUE/10 == total && Integer.MAX_VALUE %10 < digit)
            return sign == 1 ? Integer.MAX_VALUE : Integer.MIN_VALUE;

        total = 10 * total + digit;
        index ++;
    }
    return total * sign;
}

7 28 实现strStr()

就是个字符串搜索…

我的思路:直接用了之前的KMP板子(但是似乎很慢?)其实也可以考虑 RK

其他思路:直接暴力 似乎反而还更快…

public int strStr(String haystack, String needle) {
    if(needle.length()==0) return 0;
    return runKMP(haystack,needle,myNext(needle.toCharArray()));
}

public static int[] myNext(char[] p){
    int[] next = new int[p.length+1];
    next[0]=-1;
    int i=0,j=-1;
    while (i<p.length){
        if(j==-1||p[i]==p[j]){
            next[i+1]=j+1;
            i++;j++;
        }else{
            j=next[j];
        }
    }
    return next;
}

public static int runKMP(String t, String p, int[] next){
    int c1=0,c2=0;
    while(c1<t.length()){
        if(c2==-1||t.charAt(c1)==p.charAt(c2)){
            c1++;c2++;
        } else {
            c2=next[c2];
        }
        if(c2==p.length()){
            return c1-c2;
        }
    }
    return -1;
}

8 38 报数

我的思路:直接用循环 用2个StringBuilder 1个存储上一次的结果 1个计算下一轮

其他思路:基本上都相同…

public static String countAndSay(int n) {
    StringBuilder s1 = new StringBuilder("1");
    StringBuilder s2 = new StringBuilder();
    for (int i = 1; i < n; i++) {
        char digit = s1.charAt(0);
        int len = 1;
        for (int j = 1; j < s1.length(); j++) {
            if(s1.charAt(j)==digit){
                len++;
            } else {
                s2.append(Integer.toString(len));
                s2.append(digit);
                digit = s1.charAt(j);
                len = 1;
            }
        }
        s2.append(Integer.toString(len));
        s2.append(digit);
        s1.delete(0,s1.length());
        s1.append(s2);
        s2.delete(0,s2.length());
    }
    return s1.toString();
}

9 14 最长公共前缀

我的思路:直接全体一列列比较

坑:注意内外层循环 一开始我只跳出了一层

其他解法:似乎都差不多 还有二分的

也可以直接建立trie树 然后找分叉点

public static String longestCommonPrefix(String[] strs) {
    if(strs.length==0){
        return "";
    }
    if(strs.length==1){
        return strs[0];
    }
    int minlen = strs[0].length();
    for (int i = 1; i < strs.length; i++) {
        if(strs[i].length()<minlen){
            minlen = strs[i].length();
        }
    }
    int count = 0;
    outer: for (int i = 0; i < minlen; i++) {
        char c = strs[0].charAt(i);
        boolean same = true;
        for (int j = 1; j < strs.length; j++) {
            if(strs[j].charAt(i)!=c){
                same = false;
                break outer;
            }
        }
        if(same)
            count++;
    }
    return strs[0].substring(0,count);
}

猜你喜欢

转载自blog.csdn.net/nekonull/article/details/86505866