幾道 BAT 算法面試中常常問的「字符串」問題

String 做爲最多見的編程語言類型之一,在算法面試中出現的頻率極高。java

1. 驗證迴文串

題目來源於 LeetCode 第 125 號問題:驗證迴文串。這道題目是 初級程序員 在面試的時候常常遇到的一道算法題,並且面試官喜歡面試者手寫!c++

題目描述

給定一個字符串,驗證它是不是迴文串,只考慮字母和數字字符,能夠忽略字母的大小寫。git

**說明:**本題中,咱們將空字符串定義爲有效的迴文串。程序員

示例 1:面試

輸入: "A man, a plan, a canal: Panama"
輸出: true
複製代碼

示例 2:算法

輸入: "race a car"
輸出: false
複製代碼

題目解析

先理解一個概念:所謂迴文,就是一個正讀和反讀都同樣的字符串。編程

先假設是驗證單詞 level 是不是迴文字符串,經過概念涉及到 正 與 反 ,那麼很容易想到使用雙指針,從字符的開頭和結尾處開始遍歷整個字符串,相同則繼續向前尋找,不一樣則直接返回 false。數組

而這裏與單獨驗證一個單詞是不是迴文字符串有所區別的是加入了 空格 與 非字母數字的字符,但實際上的作法同樣的:bash

一開始先創建兩個指針,left 和 right , 讓它們分別從字符的開頭和結尾處開始遍歷整個字符串。編程語言

若是遇到非字母數字的字符就跳過,繼續往下找,直到找到下一個字母數字或者結束遍歷,若是遇到大寫字母,就將其轉爲小寫。

當左右指針都找到字母數字時,能夠進行比較的時候,比較這兩個字符,若是相等,則兩個指針向它們的前進方向挪動,而後繼續比較下面兩個分別找到的字母數字,若不相等,直接返回 false。

動畫描述

動畫描述

代碼實現

注:isLetterOrDigit 方法肯定指定的字符是否爲字母或數字。

class Solution {
    public boolean isPalindrome(String s) {
        if(s.length() == 0)
             return true;
        int l = 0, r = s.length() - 1;
        while(l < r){
            //肯定指定的字符是否爲字母或數字
            if(!Character.isLetterOrDigit(s.charAt(l))){
                l++;
            }else if(!Character.isLetterOrDigit(s.charAt(r))){
                r--;
            }else{
                if(Character.toLowerCase(s.charAt(l)) != Character.toLowerCase(s.charAt(r)))
                    return false;
                l++;
                r--;
            } 
        }
        return true;
    }
}

複製代碼

2. 分割回文串

題目來源於 LeetCode 第 131 號問題:分割回文串。

題目描述

給定一個字符串 s,將 s 分割成一些子串,使每一個子串都是迴文串。

返回 s 全部可能的分割方案。

示例:

輸入: "aab"
輸出:
[
  ["aa","b"],
  ["a","a","b"]
]
複製代碼

題目解析

首先,對於一個字符串的分割,確定須要將全部分割狀況都遍歷完畢才能判斷是否是迴文數。不能由於 abba 是迴文串,就認爲它的全部子串都是迴文的。

既然須要將全部的分割方法都找出來,那麼確定須要用到DFS(深度優先搜索)或者BFS(廣度優先搜索)。

在分割的過程當中對於每個字符串而言均可以分爲兩部分:左邊一個迴文串加右邊一個子串,好比 "abc" 可分爲 "a" + "bc" 。 而後對"bc"分割仍然是一樣的方法,分爲"b"+"c"。

在處理的時候去優先尋找更短的迴文串,而後回溯找稍微長一些的迴文串分割方法,不斷回溯,分割,直到找到全部的分割方法。

舉個🌰:分割"aac"。

  1. 分割爲 a + ac
  2. 分割爲 a + a + c,分割後,獲得一組結果,再回溯到 a + ac
  3. a + ac 中 ac 不是迴文串,繼續回溯,回溯到 aac
  4. 分割爲稍長的迴文串,分割爲 aa + c 分割完成獲得一組結果,再回溯到 aac
  5. aac 不是迴文串,搜索結束

動畫描述

動畫描述

代碼實現

class Solution {
    List<List<String>> res = new ArrayList<>();
    
    public List<List<String>> partition(String s) {
        if(s==null||s.length()==0)
            return res;
        dfs(s,new ArrayList<String>(),0);
        return res;
    }
    
    public void dfs(String s,List<String> remain,int left){
        if(left==s.length()){  //判斷終止條件
            res.add(new ArrayList<String>(remain));  //添加到結果中
            return;
        }
        for(int right=left;right<s.length();right++){  //從left開始,依次判斷left->right是否是迴文串
            if(isPalindroom(s,left,right)){  //判斷是不是迴文串
                remain.add(s.substring(left,right+1));   //添加到當前迴文串到list中
                dfs(s,remain,right+1);  //從right+1開始繼續遞歸,尋找回文串
                remain.remove(remain.size()-1);  //回溯,從而尋找更長的迴文串
            }
        }
    }
    /** * 判斷是不是迴文串 */
    public boolean isPalindroom(String s,int left,int right){
        while(left<right&&s.charAt(left)==s.charAt(right)){
            left++;
            right--;
        }
        return left>=right;
    }
}
複製代碼

3. 單詞拆分

題目來源於 LeetCode 第 139 號問題:單詞拆分。

題目描述

給定一個非空字符串 s 和一個包含非空單詞列表的字典 wordDict,斷定 s 是否能夠被空格拆分爲一個或多個在字典中出現的單詞。

說明:

  • 拆分時能夠重複使用字典中的單詞。
  • 你能夠假設字典中沒有重複的單詞。

題目解析

與上面的第二題 分割回文串 有些相似,都是拆分,可是若是此題採起 深度優先搜索 的方法來解決的話,答案是超時的,不信的同窗能夠試一下~

爲何會超時呢?

由於使用 深度優先搜索 會重複的計算了有些位的可拆分狀況,這種狀況的優化確定是須要 動態規劃 來處理的。

若是不知道動態規劃的,能夠看一下小吳以前的萬字長文,比較詳細的介紹了動態規劃的概念。

在這裏,只須要去定義一個數組 boolean[] memo,其中第 i 位 memo[i] 表示待拆分字符串從第 0 位到第 i-1 位是否能夠被成功地拆分。

而後分別計算每一位是否能夠被成功地拆分。

代碼實現

class Solution {
    public boolean wordBreak(String s, List<String> wordDict) {
        int n = s.length();
        int max_length=0;
        for(String temp:wordDict){
            max_length = temp.length() > max_length ? temp.length() : max_length;
        }
        // memo[i] 表示 s 中以 i - 1 結尾的字符串是否可被 wordDict 拆分
        boolean[] memo = new boolean[n + 1];
        memo[0] = true;
        for (int i = 1; i <= n; i++) {
            for (int j = i-1; j >= 0 && max_length >= i - j; j--) {
                if (memo[j] && wordDict.contains(s.substring(j, i))) {
                    memo[i] = true;
                    break;
                }
            }
        }
        return memo[n];
    }
}
複製代碼

4. 反轉字符串

題目來源於 LeetCode 第 344 號問題:反轉字符串。面試官最喜歡讓你手寫的一道算法題!

題目描述

編寫一個函數,其做用是將輸入的字符串反轉過來。輸入字符串以字符數組 char[] 的形式給出。

不要給另外的數組分配額外的空間,你必須原地修改輸入數組、使用 O(1) 的額外空間解決這一問題。

你能夠假設數組中的全部字符都是 ASCII 碼錶中的可打印字符。

示例 1:

輸入:["h","e","l","l","o"]
輸出:["o","l","l","e","h"]
複製代碼

示例 2:

輸入:["H","a","n","n","a","h"]
輸出:["h","a","n","n","a","H"]
複製代碼

題目解析

這道題沒什麼難度,直接從兩頭往中間走,同時交換兩邊的字符。注意須要白板編程寫出來便可,也注意千萬別回答一句使用 reverse() 這種高級函數來解決。。。

動畫描述

動畫

代碼實現

class Solution {
public:
    string reverseString(string s) {
        int i = 0, j = s.size() - 1;
        while (i < j){
            swap(s[i],s[j]);
            i++;
            j--;
        }
        return s;
    }
};
複製代碼

5. 把字符串轉換成整數

題目來源於劍指 offer 。

題目描述

將一個字符串轉換成一個整數,字符串不是一個合法的數值則返回 0,要求不能使用字符串轉換整數的庫函數。

題目解析

這道題要考慮全面,對異常值要作出處理。

對於這個題目,須要注意的要點有:

  • 指針是否爲空指針以及字符串是否爲空字符串;
  • 字符串對於正負號的處理;
  • 輸入值是否爲合法值,即小於等於'9',大於等於'0';
  • int爲32位,須要判斷是否溢出;
  • 使用錯誤標誌,區分合法值0和非法值0。

代碼實現

public class Solution {
    public int StrToInt(String str) {
    if (str == null || str.length() == 0)
        return 0;
    boolean isNegative = str.charAt(0) == '-';
    int ret = 0;
    for (int i = 0; i < str.length(); i++) {
        char c = str.charAt(i);
        if (i == 0 && (c == '+' || c == '-'))  /* 符號斷定 */
            continue;
        if (c < '0' || c > '9')                /* 非法輸入 */
            return 0;
        ret = ret * 10 + (c - '0');
    }
    return isNegative ? -ret : ret;
  }
}
複製代碼

更多內容

相關文章
相關標籤/搜索