- 数字当てゲーム
は2回トラバースする必要があり、最適化は2回目が1回目と関連していることです。ハッシュテーブルには1回目が含まれます
class Solution {
public:
string getHint(string secret, string guess) {
int x = 0, y = 0;
string ret;
unordered_map<char, int> map;
for (int i = 0; i < guess.size(); i++)
{
if (secret[i] == guess[i])
{
x++;
}
else
{
if (map[secret[i]] > 0)
map[secret[i]]++;
else
map[secret[i]] = 1;
}
}
for (int i = 0; i < guess.size(); i++)
{
if (map[guess[i]] > 0 && secret[i] != guess[i])
{
map[guess[i]]--;
y++;
}
}
ret += to_string(x);
ret += 'A';
ret += to_string(y);
ret += 'B';
return ret;
}
};
- 最長の昇順のサブシーケンス
順序付けられていない整数配列を指定して、最長の昇順のサブシーケンスの長さを見つけます。
動的プログラミングで簡単に解決でき、以前のdpを比較して最大値+1をとります。ただし、動的プログラミングでは、各走査を繰り返す前に各位置で最大値を見つける必要があるため、貪欲アルゴリズムを使用して解決することをお勧めします。コアアイデアは次のように要約できます。可能な限り多くの新しい数を追加してより長いサブシーケンスを形成できるように、可能な限り最小の仮数を維持します。したがって、毎回以前の位置をすべて走査する必要はありませんが、サブシーケンスレコード配列自体を走査して、古い要素を新しい要素よりも大きく置き換えます。サブシーケンスはソートされるため、二分法によって解決でき、時間の複雑さを最適化できます。
class Solution {
public:
int lengthOfLIS(vector<int>& nums) {
int size = nums.size();
if (size == 0) return 0;
vector<int> dp(size, 0);
int max = 1, dpMax;
dp[0] = 1;
for (int i = 1; i < size; i++)
{
dpMax = 1;
for (int j = 0; j < i; j++)
{
if (nums[i] > nums[j])
{
if (dp[j] + 1 > dpMax)
{
dpMax = dp[j] + 1;
}
}
}
dp[i] = dpMax;
if (dpMax > max)
max = dpMax;
}
return max;
}
};
class Solution {
public:
int lengthOfLIS(vector<int> &nums) {
int len = nums.size();
if (len < 2) {
return len;
}
vector<int> tail;
tail.push_back(nums[0]);
// tail 结尾的那个索引
int end = 0;
for (int i = 1; i < len; ++i)
{
if (nums[i] > tail[end])
{
tail.push_back(nums[i]);
end++;
}
else
{
int left = 0;
int right = end;
while (left < right)
{
int mid = (left + right) >> 1;
if (tail[mid] < nums[i])
{
left = mid + 1;
}
else
{
right = mid;
}
}
tail[left] = nums[i];
}
}
return end + 1;
}
};
- 無効なブラケットを
削除する無効なブラケットの最小数を削除して、入力文字列を有効にし、考えられるすべての結果を返します。
問題を解決するために、古典的なバックトラック方式が使用されます。文字列の有効性を判断するプロセスはディープトラバーサルプロセスに入れられ、現在取得されている結果文字列が無効になると、早期に終了します。具体的な実装は、現在の結果文字列に間違った '('および ')' cntlおよびcntrの数を記録することです。トラバーサルプロセス中にcntr> cntlが表示される場合、現在の結果文字列は無効であり、早期に終了します
class Solution {
public:
unordered_set<string> sets;
/*
* @param s: 源字符串
* @param index: 当前源字符串索引
* @param str: 记录结果
* @param el: 当前可以删除的'('的数量
* @param er: 当前可以删除的')'的数量
* @param cntl: 记录当前str中错误的'('的数量
* @param cntr: 记录当前str中错误的')'的数量
*/
void dfs(string &s, int index, string &str, int el, int er, int cntl, int cntr){
// 剪枝
if(cntr > cntl || el < 0 || er < 0) return;
// 结束条件
if(index == s.length()){
if(cntl == 0 && cntr == 0){
sets.insert(str);
}
return;
}
// 当前字符不是括号,直接跳过
if(s[index] != '(' && s[index] != ')'){
str += s[index];
dfs(s, index+1, str, el, er, cntl, cntr);
str.erase(str.length()-1, 1);
}else{
// 不删除当前括号,需要记录当前str中错误的左右括号的数量
str += s[index];
int cl = cntl, cr = cntr;
if(s[index] == '(') cl++;
else{
if(cl == 0) cr++;
else cl--;
}
dfs(s, index+1, str, el, er, cl, cr);
str.erase(str.length()-1, 1);
// 删除当前括号,修改可删除的左右括号数量
if(s[index] == '(') --el;
else --er;
dfs(s, index+1, str, el, er, cntl, cntr);
}
}
vector<string> removeInvalidParentheses(string s) {
vector<string> res;
// 统计源字符串中无效括号数目
int el = 0, er = 0;
for(int i = 0; i < s.length(); ++i){
if(s[i] == '(') el++;
else if(s[i] == ')'){
if(el == 0) er++;
else el--;
}
}
string str = "";
dfs(s, 0, str, el, er, 0, 0);
for(auto it = sets.begin(); it != sets.end(); ++it){
res.push_back(*it);
}
return res;
}
};
- 領域と取得
整数配列numsが与えられた場合、iとjを含む、インデックスiからj(i≤j)の範囲の要素の合計を見つけます。
非常に単純な質問です。事前に蓄積された変数を保存してから、それらを減算します
class NumArray {
public:
NumArray(vector<int>& nums) {
int sum = 0;
for (int i = 0; i < nums.size(); ++i)
{
sum += nums.at(i);
m_map[i] = sum;
}
}
int sumRange(int i, int j) {
return m_map[j] - m_map[i - 1]; // i == 0 key为-1不存在,会自动插入pair(-1, 0)
}
private:
unordered_map<int, int> m_map; //<索引,0到索引的元素和>
};
- 2次元の領域と取得
2次元の行列が与えられた場合、サブ長方形の範囲の要素の合計を計算します。サブ行列の左上隅は(row1、col1)であり、右下隅は(row2、col2)です。
この質問は基本的に前の質問と同じです:4つの固定小数点の領域を0,0にバッファーしてから、それを減算します。
class NumMatrix {
public:
NumMatrix(vector<vector<int>>& matrix) {
rw = matrix.size();
cl = rw ? matrix[0].size() : 0;
sumMatrix = vector<vector<int>>(rw + 1, vector<int> (cl + 1, 0));
for (int i = 1; i < rw + 1; ++i){
for (int j = 1; j < cl + 1; ++j){
sumMatrix[i][j] = matrix[i - 1][j - 1] +
sumMatrix[i - 1][j] + sumMatrix[i][j - 1] - sumMatrix[i - 1][j - 1];
}
}
}
int sumRegion(int row1, int col1, int row2, int col2) {
return sumMatrix[row2 + 1][col2 + 1] - sumMatrix[row1][col2 + 1] - sumMatrix[row2 + 1][col1] + sumMatrix[row1][col1];
}
private:
int rw, cl;
vector<vector<int>> sumMatrix;
};
/**
* Your NumMatrix object will be instantiated and called as such:
* NumMatrix* obj = new NumMatrix(matrix);
* int param_1 = obj->sumRegion(row1,col1,row2,col2);
*/
- 累積数
累積数は文字列であり、それを構成する数は累積シーケンスを形成できます。
有効な累積シーケンスには、少なくとも3つの数値が含まれている必要があります。最初の2つの数値を除いて、文字列内の他の数値は、前の2つの数値の合計と等しくなります。
数値「0」〜「9」のみを含む文字列を指定して、指定された入力が累積数であるかどうかを判断するアルゴリズムを記述します。
**この質問は典型的なバックトラックのアイデアです。トラバーサルの長さは半分にすぎません。長いと、最初に形成される数は2番目の数の合計にならず、長さが足りません。**
class Solution {
public:
bool isAdditiveNumber(string num) {
int len = num.size();
for(int i = 1; i <= len/2; i++){
// 为了排除“0235813”
if(i>1 && num[0]=='0')
break;
for(int j = 1; j + i < len;j++){
// 这里需要排除第二个数以0开头的情况,dfs里面不需要判断,因为加完的数开始肯定没有0,如果字符串里面有那么找到的位置也不是index3 + len3
if(!(j>1 && num[i]=='0') && dfs(num,0,i,stol(num.substr(0,i)),i,j,stol(num.substr(i,j)),len))
return true;
}
}
return false;
}
// 当前要加的两个数的索引和长度
bool dfs(string& num, int index2,int len2,long num2,int index3,int len3,long num3, int &len){
// 如果第二个数的索引加上他的长度就等于num的长度,就说明到末尾了,返回true
if(index3 + len3 == len)
return true;
long sum = num2 + num3;
string sSum = to_string(sum);
// 将两个数加起来,在剩下的字符串中去寻找这个和,如果在开始的位置就找到,就说明可以继续下去
if(num.find(sSum,index3+len3)!=index3+len3)
return false;
return dfs(num,index3,len3,num3,index3+len3,sSum.size(),sum,len);
}
};