力扣解题思路:字符串

205. 同构字符串


思路:给定两个字符串 s 和 t,判断它们是否是同构的。如果 s 中的字符可以被替换得到 t ,那么这两个字符串是同构的。所有出现的字符都必须用另一个字符替换,同时保留字符的顺序。两个字符不能映射到同一个字符上,但字符可以映射自己本身。
我们比较直观的想法是用map,将对应位置形成映射关系,下次出现相同的单词对比map中的值即可,如果不一样,直接返回false,如果map中没有这个映射!map.containsKey(s.charAt(i))时,就要考虑是否map.containsValue(t.charAt(i)),这是为了排除两个字符映射到同一个字符上,这种情况直接返回false。

public boolean isIsomorphic(String s, String t) {
    if(s.length() != t.length()){
        return false;
    }  
    HashMap<Character, Character> map = new HashMap<>();
    for(int i=0; i<s.length(); i++){
        if(!map.containsKey(s.charAt(i))){
            if(map.containsValue(t.charAt(i))){
                return false;
            }
            map.put(s.charAt(i), t.charAt(i));
        }else{
            if(map.get(s.charAt(i))!=t.charAt(i)){
                return false;
            }
        }
    } 
    return true;
}

那有没有更简洁的方法呢?我们可以用两个数组来记录这两个字符串每个字符最后出现的映射,每次只用和最后一次的对比即可,而且这种不需要进行是否两个字符映射到同一个字符上的判断,因为如果存在则上一次的映射的字符必定不会满足这两个数组对应位置相等(只有都没有映射时他们会相等于0,或者满足一个映射时才会值相等,如果该映射之前就存在,那么必不可能满足相等条件),完整代码如下:

public boolean isIsomorphic(String s, String t) {
    int[] preIndexOfS = new int[256];
    int[] preIndexOfT = new int[256];
    for (int i = 0; i < s.length(); i++) {
        char sc = s.charAt(i), tc = t.charAt(i);
        if (preIndexOfS[sc] != preIndexOfT[tc]) {
            return false;
        }
        preIndexOfS[sc] = i + 1;
        preIndexOfT[tc] = i + 1;
    }
    return true;
}

我们可以举一个例子验证我的说法,如egg和aaa,在比较第0个字符时是相等的,再比较第二个时,第二个数组的a已经赋值,是不可能等于第一个数组的g位置的,因为g位置是0。

647. 回文子串


思路:举个简单的例子:

Input: "aaa"
Output: 6
Explanation: Six palindromic strings: "a", "a", "a", "aa", "aa", "aaa".

(方法1:递归)首先我们要知道,对于一个回文字符串,他的回文子串是有很多的(比如从他的中心开始延申),所以我们第一个思路是,将字符串中的每个字符都作为中心向外延申判断回文字符串的数量。
但是有一个问题,回文字符串一定是有中心的吗?当然是不一定的,所以我们应该也要考虑中心是没有字符的情况,也就是分为奇数长度和偶数长度

public int cnt = 0;

public int countSubstrings(String s) {
    for (int i = 0; i < s.length(); i++) {
        extendSubstrings(s, i, i);     // 奇数长度
        extendSubstrings(s, i, i + 1); // 偶数长度
    }
    return cnt;
}

然后从start和end开始分别向左向右判断:

public void extendSubstrings(String s, int start, int end) {
    while (start >= 0 && end < s.length() && s.charAt(start) == s.charAt(end)) {
        start--;
        end++;
        cnt++;
    }
}

(方法2:动态规划)当然,我们也可以使用动态规划的方法来解题,dp[i][j] 表示[i,j]的字符是否为回文子串,如果s.charAt(i)==s.charAt(j)并且dp[i+1][j-1]也是回文串,那么dp[i][j]也是回文子串,这时候回文串的个数就加一
这里还需要注意的是,dp[i][j]依赖于dp[i+1][j-1],所以dp[i+1][j-1]得先于dp[i][j]判断,也就是i应该递减更新,而j应该递增更新,完整代码如下:

public int countSubstrings(String s) {
    int res = 0;
    int n = s.length();
    boolean[][] dp = new boolean[n][n];
    for(int i=n-1; i>=0; i--){ 
        for(int j=i; j<n; j++){
            if(s.charAt(i)==s.charAt(j) && (j-i<=2 || dp[i+1][j-1])){
                dp[i][j] = true;
                res++;
            }
        }
    }  
   return res; 
}

其中,当s.charAt(i)==s.charAt(j) 时,当元素个数为1,2,3个时,一定为回文子串,也就是单个字符也是回文串。

9. 回文数


思路:判断一个整数是否是回文数。回文数是指正序(从左向右)和倒序(从右向左)读都是一样的整数。
先写个最简单的方法转成字符串直接判断,数字末尾有0的直接排除:

public boolean isPalindrome(int x) {
    if (x < 0 || (x % 10 == 0 && x != 0)) return false;
    String s = String.valueOf(x);
    int i=0;
    int j=s.length()-1;
    while(i<j){
        if(s.charAt(i) == s.charAt(j)){
            i++;
            j--;
        }else{
            return false;
        }
    }
    return true;
}

如果不能使用额外空间如何判断呢?我们可以将整数分成左右两部分,右边那部分需要转置,然后判断这两部分是否相等。

    while (x > right) {
        right = right * 10 + x % 10;
        x /= 10;
    }

这样就可以获得右边部分了,但此时需要注意的是,这时的右边部分可能最后包含0,因为可能该数中间就是0,那么我们应该除去0再把x和right相比较,完整代码如下:

public boolean isPalindrome(int x) {
    if (x == 0) {
        return true;
    }
    if (x < 0 || x % 10 == 0) {
        return false;
    }
    int right = 0;
    while (x > right) {
        right = right * 10 + x % 10;
        x /= 10;
    }
    return x == right || x == right / 10;
}

696. 计数二进制子串


思路:给定一个字符串 s,计算具有相同数量0和1的非空(连续)子字符串的数量,并且这些子字符串中的所有0和所有1都是组合在一起的。重复出现的子串要计算它们出现的次数。

Input: "00110011"
Output: 6
Explanation: There are 6 substrings that have equal number of consecutive 1's and 0's: "0011", "01", "1100", "10", "0011", and "01".

我们可以用last来记录之前一种数字的个数, cur来记录当前数字的个数; last >= cur的时候, res ++; 具体代码如下:

public int countBinarySubstrings(String s) {
    int last = 0;
    int cur = 1;
    int res = 0;
    for(int i=1;i<s.length();i++){
        if(s.charAt(i)==s.charAt(i-1)){
            cur++;
        }else{
            last = cur;
            cur = 1;
        }
        if(last>=cur) res++;
    }
    return res;
}

nowCoder:翻转单词顺序列


思路:题目应该/有一个隐含条件,就是不能用额外的空间。虽然 Java 的题目输入参数为 String 类型,需要先创建一个字符数组使得空间复杂度为 O(N),但是正确的参数类型应该和原书一样,为字符数组,并且只能使用该字符数组的空间。任何使用了额外空间的解法在面试时都会大打折扣,包括递归解法。正确的解法应该是: 先旋转每个单词,再旋转整个字符串。
这里还需要注意,不可以直接再字符串上直接操作,要使用数组噢ε
=ε=ε=(  ̄▽ ̄)

Input:
"I am a student."

Output:
"student. a am I"
public String ReverseSentence(String str) {
    int n = str.length();
    char[] chars = str.toCharArray();
    int i = 0, j = 0;
    while (j <= n) {//这里j可以等于n是因为为了统一j == n 和 chars[j] == ' '的情况,reverse(chars, i, j - 1)中可以看出,反转时并不包含j,所以是可以这么写的,当然如果分开写这两种情况也是可以的o(* ̄▽ ̄*)o
        if (j == n || chars[j] == ' ') {
            reverse(chars, i, j - 1);
            i = j + 1;
        }
        j++;
    }
    reverse(chars, 0, n - 1);
    return new String(chars);
}

private void reverse(char[] c, int i, int j) {
    while (i < j)
        swap(c, i++, j--);
}

private void swap(char[] c, int i, int j) {
    char t = c[i];
    c[i] = c[j];
    c[j] = t;
}

这一题类似于力扣151. 翻转字符串里的单词,但是力扣这题所给的字符串中间有的有超过一个的空格,需要清理。

private String clean_space(char[] s_arr, int n) {
    int i = 0;
    int j = 0;
    while (j < n) {
        while (j < n && s_arr[j] == ' ') j++;
        while (j < n && s_arr[j] != ' ') s_arr[i++] = s_arr[j++];
        while (j < n && s_arr[j] == ' ') j++;
        if (j < n) s_arr[i++] = ' ';
    }
    return new String(s_arr).substring(0, i);
}

再次提醒,new String中参数可以是char数组噢~

43. 字符串相乘

思路:给定两个以字符串形式表示的非负整数 num1 和 num2,返回 num1 和 num2 的乘积,它们的乘积也表示为字符串形式。

    public String multiply(String num1, String num2) {
        /**
        num1的第i位(高位从0开始)和num2的第j位相乘的结果在乘积中的位置是[i+j, i+j+1]
        例: 123 * 45,  123的第1位 2 和45的第0位 4 乘积 08 存放在结果的第[1, 2]位中
          index:    0 1 2 3 4  
              
                        1 2 3
                    *     4 5
                    ---------
                          1 5
                        1 0
                      0 5
                    ---------
                      0 6 1 5
                        1 2
                      0 8
                    0 4
                    ---------
                    0 5 5 3 5
        这样我们就可以单独都对每一位进行相乘计算把结果存入相应的index中        
        **/
        int n1 = num1.length()-1;
        int n2 = num2.length()-1;
        if(n1 < 0 || n2 < 0) return "";
        int[] mul = new int[n1+n2+2];
        for(int i = n1; i >= 0; --i) {
            for(int j = n2; j >= 0; --j) {
                int bitmul = (num1.charAt(i)-'0') * (num2.charAt(j)-'0');      
                bitmul += mul[i+j+1]; // 先加低位判断是否有新的进位
                mul[i+j] += bitmul / 10;
                mul[i+j+1] = bitmul % 10;//bitmul += mul[i+j+1]已完成,所以此时是=而不是+=
            }
        }
        StringBuilder sb = new StringBuilder();
        int i = 0;
        // 去掉前导0
        while(i < mul.length-1 && mul[i] == 0) 
            i++;
        for(; i < mul.length; ++i)
            sb.append(mul[i]);
        return sb.toString();
    }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值