力扣刷題筆記(六)

唉,昨天沒有寫這個系列的,被KMP給卡住了,不過還好,突破了。

照例三題,你們等下就會看到KMP算法了,實在是鬼斧生工。

第一題:實現strStr

實現 strStr() 函數。

給定一個 haystack 字符串和一個 needle 字符串,在 haystack 字符串中找出 needle 字符串出現的第一個位置 (從0開始)。如果不存在,則返回 -1。

示例 1:

輸入: haystack = “hello”, needle = “ll”
輸出: 2
示例 2:

輸入: haystack = “aaaaa”, needle = “bba”
輸出: -1
說明:

當 needle 是空字符串時,我們應當返回什麼值呢?這是一個在面試中很好的問題。

對於本題而言,當 needle 是空字符串時我們應當返回 0 。這與C語言的 strstr() 以及 Java的 indexOf() 定義相符。

來源:力扣(LeetCode) 鏈接:https://leetcode-cn.com/problems/implement-strstr
著作權歸領釦網絡所有。商業轉載請聯繫官方授權,非商業轉載請註明出處。


第二題:外觀數列

「外觀數列」是一個整數序列,從數字 1 開始,序列中的每一項都是對前一項的描述。前五項如下:

  1. 1
    
  2. 11
    
  3. 21
    
  4. 1211
    
  5. 111221
    

1 被讀作 “one 1” (“一個一”) , 即 11。
11 被讀作 “two 1s” (“兩個一”), 即 21。
21 被讀作 “one 2”, “one 1” (“一個二” , “一個一”) , 即 1211。

給定一個正整數 n(1 ≤ n ≤ 30),輸出外觀數列的第 n 項。

注意:整數序列中的每一項將表示爲一個字符串。

示例 1:

輸入: 1
輸出: “1”
解釋:這是一個基本樣例。
示例 2:

輸入: 4
輸出: “1211”
解釋:當 n = 3 時,序列是 “21”,其中我們有 “2” 和 “1” 兩組,“2” 可以讀作 “12”,也就是出現頻次 = 1 而 值 = 2;類似 “1” 可以讀作 “11”。所以答案是 “12” 和 “11” 組合在一起,也就是 “1211”。

來源:力扣(LeetCode) 鏈接:https://leetcode-cn.com/problems/count-and-say
著作權歸領釦網絡所有。商業轉載請聯繫官方授權,非商業轉載請註明出處。


第三題:atoi實現

請你來實現一個 atoi 函數,使其能將字符串轉換成整數。

首先,該函數會根據需要丟棄無用的開頭空格字符,直到尋找到第一個非空格的字符爲止。接下來的轉化規則如下:

如果第一個非空字符爲正或者負號時,則將該符號與之後面儘可能多的連續數字字符組合起來,形成一個有符號整數。
假如第一個非空字符是數字,則直接將其與之後連續的數字字符組合起來,形成一個整數。
該字符串在有效的整數部分之後也可能會存在多餘的字符,那麼這些字符可以被忽略,它們對函數不應該造成影響。
注意:假如該字符串中的第一個非空格字符不是一個有效整數字符、字符串爲空或字符串僅包含空白字符時,則你的函數不需要進行轉換,即無法進行有效轉換。

在任何情況下,若函數不能進行有效的轉換時,請返回 0 。

提示:

本題中的空白字符只包括空格字符 ’ ’ 。
假設我們的環境只能存儲 32 位大小的有符號整數,那麼其數值範圍爲 [−231, 231 − 1]。如果數值超過這個範圍,請返回 INT_MAX (231 − 1) 或 INT_MIN (−231) 。

示例 1:

輸入: “42”
輸出: 42
示例 2:

輸入: " -42"
輸出: -42
解釋: 第一個非空白字符爲 ‘-’, 它是一個負號。
我們儘可能將負號與後面所有連續出現的數字組合起來,最後得到 -42 。
示例 3:

輸入: “4193 with words”
輸出: 4193
解釋: 轉換截止於數字 ‘3’ ,因爲它的下一個字符不爲數字。
示例 4:

輸入: “words and 987”
輸出: 0
解釋: 第一個非空字符是 ‘w’, 但它不是數字或正、負號。
因此無法執行有效的轉換。
示例 5:

輸入: “-91283472332”
輸出: -2147483648
解釋: 數字 “-91283472332” 超過 32 位有符號整數範圍。
因此返回 INT_MIN (−231) 。

來源:力扣(LeetCode)
鏈接:https://leetcode-cn.com/problems/string-to-integer-atoi
著作權歸領釦網絡所有。商業轉載請聯繫官方授權,非商業轉載請註明出處。


我的題解(1) --KMP算法

總算是徹底弄清楚了,KMP算法

代碼就不放這篇了,放了也基本看不懂,解釋的話沒幾千字解釋不清楚,所以我就把我的KMP博客鏈接放這兒吧。


我的題解(2)–遞歸

我的好兄弟他跟我說看不懂這道題,讓我趕緊解決KMP之後過來,我一看,這不是很簡單嗎!!!

這麼說把,這道題,不管你給的數字是多少,最後肯定要回到1,從1出發。也不要想着什麼暴力算法,我試過了,給你個9,你就得輸出14個字符,那給你個30你是不是得輸出上百個字符啊。

既然都要回到1,又是重1出發一層一層嵌套,那很直觀的想法就是遞歸嘛。說實話這是我爲數不多的用遞歸的情況,之前在快排、上面那個KMP用到,好像就沒了。

代碼中已經帶上了我的思考,當然,老規矩,流程圖不能少。

在這裏插入圖片描述

#include<iostream>
#include<string>
#include<vector>

using namespace std;

//先描述一個簡單的字符串吧
string show(string str,int n)
{	
	//遞歸結束條件
	if (n == 0)
	{
		return str;
	}

	int fast = 0, slow = 0;//快慢指針
	int sz = str.size();
	int count = 0;
	string new_str;

	while(slow < sz)
	{	
		if (fast<sz && str[fast] == str[slow])
		{
			count++;
			fast++;
		}
		else
		{
			new_str.push_back(count+48);
			new_str.push_back(str[slow]);
			count = 0;
			slow = fast;
		}
	}
	n--;
	new_str = show(new_str,n);	//前面不是return了?爲什麼會繞回來這裏?哦,遞歸,原來是這樣,只是第二層return了,當然回到前一層
	return new_str;
}

string countAndSay(int n) {
	if (n == 1)
		return "1";
	else
	{
		string s = "1";
		string t = show(s, n-1);	//這裏的n對應的是n+1,因爲n=1是個特殊情況
		return t;
	}
}

int main()
{
	string a = countAndSay(2);
	cout << a << endl;
}

我的題解(3)

這題其實沒什麼難的,就是細節很多,所以排在了中等難度,我就拿過來了。
唉,懺愧,提交了12次。

#include<iostream>
#include<string>

using namespace std;

int myAtoi(string str) 
{
	int sz = str.size();
	string str_temp;
	int zf_flag = 1;	//正數or複數//emmm,或者就一個正號

	for (int a = 0; a < sz; a++)
	{
		//進入取值地界了
		if (str[a] != 32)
		{
			//不是從這裏進,從第一個非空格1進
			if (str[a] == 45 || str[a] == 43 || (str[a] >= 48 && str[a] <= 57))
			{
				if (str[a] == 45)
				{
					zf_flag = -1;
					a++;
				}
				else if (str[a] == 43)
				{
					zf_flag = 0;
					a++;
				}

				while(str[a] == 48)	//把0濾掉
					a++;
				
				while (str[a] >= 48 && str[a] <= 57)
				{
					str_temp.push_back(str[a]);
					a++;
				}
				break;	//退出循環
			}
			else
				break;
		}
	}

	//接下來對取出的值進行操作

	int temp_size = str_temp.size();
	int ret = 0;
	int i = 0;
	int pow_num = 0;
	//對負數進行操作
	if (zf_flag == -1)
	{
		if (temp_size == 0)
			return 0;

		if (temp_size < 10)
		{
			while (temp_size > 0)
			{
				temp_size--;
				pow_num = pow(10, temp_size);
				ret += (int)(str_temp[i] - 48) * pow_num;
				i++;
			}

			return (-1)*ret;
		}
		//判斷臨界條件
		else if (temp_size == 10)
		{
			if (str_temp[0] > 50)
				return INT_MIN;

			ret = INT_MIN;
			while (temp_size > 0)
			{
				temp_size--;
				pow_num = pow(10, temp_size);
				ret += (int)(str_temp[i] - 48) * pow_num;
				i++;
			}

			if (ret <= 0)
				return (-1)*(ret - INT_MIN);
			else
				return INT_MIN;
		}
		else
			return INT_MIN;
	}

	//對正數進行操作
	if (zf_flag == 1 || zf_flag == 0)
	{
		if (temp_size == 0)
			return 0;
		if (temp_size < 10)
		{
			while (temp_size > 0)
			{
				temp_size--;
				pow_num = pow(10, temp_size);
				ret += (int)(str_temp[i] - 48) * pow_num;
				i++;
			}

			return ret;
		}
		else if (temp_size == 10)
		{
			if(str_temp[0]>50)
				return INT_MAX;

			ret = INT_MAX;
			while (temp_size > 0)
			{
				temp_size--;
				pow_num = pow(10, temp_size);
				ret -= (int)(str_temp[i] - 48) * pow_num;
				i++;
			}

			if (ret <= 0)
				return INT_MAX;
			else
				return (INT_MAX-ret);
		}
		else
			return INT_MAX;
	}

	return 0;
}

int main()
{
	//int a = '9';	//57
	//int b = '0';	//48
	//int c = '-';	//45
	//int d = ' ';	//32
	//int e = '+';	//43
	int d = myAtoi("2147483633");
	cout << d << endl;
	return 0;
}

官方題解(1) – Rabin Karp - 常數複雜度

方法恆強,反正我是沒看懂。上次(前天)說要搞定哈希表之後,就被快排拖住了,解決了快排之後,又讓KMP卡住了,所以哈希表我到現在還不會用。所以就看不懂了。

有一種最壞時間複雜度也爲 O(N)O(N) 的算法。思路是這樣的,先生成窗口內子串的哈希碼,然後再跟 needle 字符串的哈希碼做比較。

這個思路有一個問題需要解決,如何在常數時間生成子串的哈希碼?

在這裏插入圖片描述

在這裏插入圖片描述

算法

計算子字符串 haystack.substring(0, L) 和 needle.substring(0, L) 的哈希值。

從起始位置開始遍歷:從第一個字符遍歷到第 N - L 個字符。

根據前一個哈希值計算滾動哈希。

如果子字符串哈希值與 needle 字符串哈希值相等,返回滑動窗口起始位置。

返回 -1,這時候 haystack 字符串中不存在 needle 字符串。

實現

PythonJava

class Solution:
    def strStr(self, haystack: str, needle: str) -> int:
        L, n = len(needle), len(haystack)
        if L > n:
            return -1
        
        # base value for the rolling hash function
        a = 26
        # modulus value for the rolling hash function to avoid overflow
        modulus = 2**31
        
        # lambda-function to convert character to integer
        h_to_int = lambda i : ord(haystack[i]) - ord('a')
        needle_to_int = lambda i : ord(needle[i]) - ord('a')
        
        # compute the hash of strings haystack[:L], needle[:L]
        h = ref_h = 0
        for i in range(L):
            h = (h * a + h_to_int(i)) % modulus
            ref_h = (ref_h * a + needle_to_int(i)) % modulus
        if h == ref_h:
            return 0
              
        # const value to be used often : a**L % modulus
        aL = pow(a, L, modulus) 
        for start in range(1, n - L + 1):
            # compute rolling hash in O(1) time
            h = (h * a - h_to_int(start - 1) * aL + h_to_int(start + L - 1)) % modulus
            if h == ref_h:
                return start

        return -1

複雜度分析

時間複雜度:O(N)O(N),計算 needle 字符串的哈希值需要 O(L)O(L) 時間,之後需要執行 (N - L)(N−L) 次循環,每次循環的計算複雜度爲常數。

空間複雜度:O(1)O(1)。

作者:LeetCode
鏈接:https://leetcode-cn.com/problems/implement-strstr/solution/shi-xian-strstr-by-leetcode/
來源:力扣(LeetCode) 著作權歸作者所有。商業轉載請聯繫作者獲得授權,非商業轉載請註明出處。

難搞。


官方題解(2)

無,我的代碼時空複雜度都超過百分百的提交了。看我的吧。


官方題解(3) 有限狀態機(DFA)

難怪難度排到中等。

方法一:自動機
思路

字符串處理的題目往往涉及複雜的流程以及條件情況,如果直接上手寫程序,一不小心就會寫出極其臃腫的代碼。

因此,爲了有條理地分析每個輸入字符的處理方法,我們可以使用自動機這個概念:

我們的程序在每個時刻有一個狀態 s,每次從序列中輸入一個字符 c,並根據字符 c 轉移到下一個狀態 s’。這樣,我們只需要建立一個覆蓋所有情況的從 s 與 c 映射到 s’ 的表格即可解決題目中的問題。

算法

本題可以建立如下圖所示的自動機:
在這裏插入圖片描述
我們也可以用下面的表格來表示這個自動機:

接下來編程部分就非常簡單了:我們只需要把上面這個狀態轉換表抄進代碼即可。

另外自動機也需要記錄當前已經輸入的數字,只要在 s’ 爲 in_number 時,更新我們輸入的數字,即可最終得到輸入的數字。

在這裏插入圖片描述

INT_MAX = 2 ** 31 - 1
INT_MIN = -2 ** 31

class Automaton:
    def __init__(self):
        self.state = 'start'
        self.sign = 1
        self.ans = 0
        self.table = {
            'start': ['start', 'signed', 'in_number', 'end'],
            'signed': ['end', 'end', 'in_number', 'end'],
            'in_number': ['end', 'end', 'in_number', 'end'],
            'end': ['end', 'end', 'end', 'end'],
        }
        
    def get_col(self, c):
        if c.isspace():
            return 0
        if c == '+' or c == '-':
            return 1
        if c.isdigit():
            return 2
        return 3

    def get(self, c):
        self.state = self.table[self.state][self.get_col(c)]
        if self.state == 'in_number':
            self.ans = self.ans * 10 + int(c)
            self.ans = min(self.ans, INT_MAX) if self.sign == 1 else min(self.ans, -INT_MIN)
        elif self.state == 'signed':
            self.sign = 1 if c == '+' else -1

class Solution:
    def myAtoi(self, str: str) -> int:
        automaton = Automaton()
        for c in str:
            automaton.get(c)
        return automaton.sign * automaton.ans


作者:LeetCode-Solution
鏈接:https://leetcode-cn.com/problems/string-to-integer-atoi/solution/zi-fu-chuan-zhuan-huan-zheng-shu-atoi-by-leetcode-/
來源:力扣(LeetCode)
著作權歸作者所有。商業轉載請聯繫作者獲得授權,非商業轉載請註明出處。

總結

這些題做完,我們應該掌握遞歸算法,熟悉KMP算法,然後準備去學DFA了!!!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章