日常鴿比賽,不過做完這周的題後感覺挺有意思的,記一下思路。
Valid Tic-Tac-Toe State
問題
給出一個井字棋的棋盤,求是否合理。
思路
首先,井字棋先手爲X
,後手爲O
,因此合理的棋盤中X
數量比O
數量多1
或兩者相等,如果先手獲勝,則X
數量必然比O
數量多1
;如果後手獲勝,則X
數量必然和O
數量相等。其次,一旦有一方獲勝,即任意一行或一列或對角線均爲X
或均爲O
,則遊戲結束,因此棋盤不能同時滿足雙方的勝利條件。我一開始還想到一種情況,就是一方獲勝後繼續遊戲,導致棋盤中只有一方勝利,這種情況滿足前面的條件,但實際上不合理,所以要特別考慮。後來我發現要滿足這種情況至少要有六個相同的符號:
XOO
XXO
XXX
此時棋盤中只有X
獲勝,但事實上無論第六個X
放在哪個位置,前五個X
已經滿足勝利條件了,因此這種情況是不合理的,如果僅根據勝利條件來判斷棋盤是否合理會造成誤判。不過,如果考慮X
和O
的數量關係就會得到正確的結果,因此不需要再當作特殊情況對待。
代碼
class Solution {
public:
bool validTicTacToe(vector<string>& board) {
int xCount = 0, oCount = 0; //X和O的數量
bool x3 = false, o3 = false; //X和O的勝利條件
for (int i = 0; i < 3; ++i) {
for (int j = 0; j < 3; ++j) {
if (board[i][j] == 'X') ++xCount;
else if (board[i][j] == 'O') ++oCount;
}
if (board[i][0] == 'X' && board[i][1] == 'X' && board[i][2] == 'X') x3 = true;
if (board[0][i] == 'X' && board[1][i] == 'X' && board[2][i] == 'X') x3 = true;
if (board[i][0] == 'O' && board[i][1] == 'O' && board[i][2] == 'O') o3 = true;
if (board[0][i] == 'O' && board[1][i] == 'O' && board[2][i] == 'O') o3 = true;
}
if (board[0][0] == 'X' && board[1][1] == 'X' && board[2][2] == 'X') x3 = true;
if (board[0][2] == 'X' && board[1][1] == 'X' && board[2][0] == 'X') x3 = true;
if (board[0][0] == 'O' && board[1][1] == 'O' && board[2][2] == 'O') o3 = true;
if (board[0][2] == 'O' && board[1][1] == 'O' && board[2][0] == 'O') o3 = true;
if (o3 && x3) return false; //雙方同時獲勝
if (xCount > oCount + 1 || xCount < oCount) return false; //X和O數量關係不合理
else if (xCount == oCount + 1 && o3) return false; //X數量比O數量多1不可能後手獲勝
else if (xCount == oCount && x3) return false; //X數量和O數量相等不可能先手獲勝
return true;
}
};
Number of Matching Subsequences
問題
給出一個字符串和一組序列,求字符串的子序列數量。
思路
求一個序列是否爲字符串的子序列很簡單,只需要遍歷字符串就可以了,假設字符串長度n
,序列長度m
,那麼時間複雜度爲O(n)
,已經達到最優。但如果對每一個序列都遍歷字符串,假設序列數爲l
,那麼時間複雜度爲O(l*n)
,這是最差的時間複雜度,很有可能超時。我一開始想用動態規劃,記錄字符串中每一個字母的下一個任意字母的最近位置,然後時間複雜度爲O(l*m)
,這是最優的時間複雜度,但空間複雜度爲O(26*n)
,不符合題目要求。後來我用26
個隊列記錄每個序列的匹配情況,只需要遍歷一次字符串就可以判斷子序列的數量了,時間複雜度爲O(l*m)
,具體步驟如下:
1.取每個序列第一個字母對應隊列,記錄當前序列以及當前位置並插入隊列。
2.取字符串第一個字母對應隊列,取出隊列中所有元素視爲匹配成功,記錄對應每個序列的下一個位置並插入下一個字母的對應隊列,即讓每個匹配當前字母成功的序列等待下一次匹配。
3.遍歷字符串所有字母,判斷字符串的子序列數量。
代碼
class Solution {
public:
int numMatchingSubseq(string S, vector<string>& words) {
int ans = 0;
vector<queue<pair<int, int>>> v(26);
for (int i = 0; i < words.size(); ++i) {
v[words[i][0]-'a'].push({i, 1});
}
for (int i = 0; i < S.size(); ++i) {
int s = v[S[i]-'a'].size();
for (int j = 0; j < s; ++j) {
auto p = v[S[i]-'a'].front();
v[S[i]-'a'].pop();
if (p.second == words[p.first].size()) ++ans;
else v[words[p.first][p.second]-'a'].push({p.first, p.second+1});
}
}
return ans;
}
};
Number of Subarrays with Bounded Maximum
問題
給出一個序列和上下界,求最大項在上下界之間的連續的子序列數量。
思路
遍歷序列,計算以每個元素結尾的符合要求的子序列數量並相加。如果當前元素大於上界,那麼以當前元素結尾的子序列數量爲0
;如果當前元素在上下界之間,那麼任意以當前元素結尾且不包含大於上界元素的子序列均符合要求,即從上一個大於上界元素的下一個元素開始到當前元素的數量;如果當前元素小於下界,那麼任意以當前元素結尾且不包含大於上界元素且包含在上下界之間元素的子序列均符合要求,即從上一個大於上界元素的下一個元素開始到上一個在上下界之間元素的數量。
代碼
class Solution {
public:
int numSubarrayBoundedMax(vector<int>& A, int L, int R) {
int ans = 0;
int a = 0; //以當前元素結尾且符合要求的子序列數量
int b = 0; //連續的小於下界元素的數量
for (int i = 0; i < A.size(); ++i) {
if (A[i] > R) a = b = 0;
else if (A[i] < L) ++b;
else {
a += b+1;
b = 0;
}
ans += a;
}
return ans;
}
};
Preimage Size of Factorial Zeroes Function
問題
n!
末尾的0
的數量爲K
,求符合要求的n
的數量。
思路
n! = 1*2*3*···*n
,因此n!
末尾的0
的數量與質因子2
和5
的數量有關。每有一個質因子2
和一個質因子5
,n!
末尾就會有一個0
,由於質因子2
的數量大於質因子5
的數量,所以n!
末尾的0
的數量等於質因子5
的數量。假設m >= 0
,那麼(5*m+1)!,(5*m+2)!,(5*m+3)!,(5*m+4)!,(5*m+5)!
末尾的0
的數量相同,所以符合要求的n
的數量要麼爲0
,要麼爲5
。當K = 1,2,3,4,6,···
時n
的數量爲5
,當K = 5,···
時n
的數量爲0
,因爲n!
的第1
個含質因子5
的因子爲5
,第2
個因子爲10
,第3
個因子爲15
,第4
個因子爲20
,第5
個因子爲25
,由於25
有兩個質因子5
,所以不存在n!
有且僅有5
個質因子5
。以此類推,不存在n!
有且僅有30
個質因子5
,因爲第25
個因子爲125
,125
有三個質因子5
。將K
作類似於進制的轉換,我發現對於權1,6,31,···
,如果有任意一個的係數爲5
,那麼對應的n!
不存在。
代碼
class Solution {
public:
int preimageSizeFZF(int K) {
vector<int> v(15);
v[0] = 1;
for (int i = 1; i < 14; ++i) {
v[i] = 5*v[i-1] + 1; //獲得每一個權
}
for (int i = 13; i >= 0; --i) {
if (K / v[i] == 5) return 0;
else K %= v[i];
}
return 5;
}
};
總結
這次的題目非常有意思,我通過找規律得到了解題方法,但其中的原理並不能完全領會,還有待證明。