記事ディレクトリ
413. 等差数列部
クリックして表示:等差数列の除算
シーケンスに少なくとも 3 つの要素があり、隣接する 2 つの要素の差が同じである場合、そのシーケンスは算術シーケンスであると言われます。
たとえば、[1,3,5,7,9]、[7,7,7,7]、[3,-1,-5,-9] は等差数列です。
整数の配列 nums を指定すると、配列 nums 内の算術配列である部分配列の数を返します。
サブ配列は、配列内の連続したシーケンスです。
例 1:
入力: nums = [1,2,3,4]
出力: 3
説明: nums には 3 つのサブ配列があります: [1, 2, 3]、[2, 3, 4]、および [1,2, 3] ,4]自体。
例 2:
入力: nums = [1]
出力: 0
状態遷移方程式
dp[i]: i 位置要素で終わるすべての部分配列の等差数列の数を示します。
ABCD が算術数列で、D と BC も算術数列を形成できる場合、ABCDE も算術数列になります。
i で終わるすべてのサブ配列の等差級数の数を見つけたい場合、
サブ配列が連続している場合、等差級数を形成したい場合は、少なくとも位置 i と位置 i-1 および i-2 が形成されます。算術級数
dp[i] は 2 つの場合に分けられます
ケース 1: i i-1 i-2 位置要素は等差数列を形成できる
i-2 位置の要素を a、i-1 位置の要素を b、i 位置の要素を c とすると、これら 3 つの
差は同じです。つまり、cb==ba
v ab で終わる算術シーケンス。c と ab も算術シーケンスを形成できるため、abc で終わることも算術シーケンスであり、
ab で終わることは b で終わること、つまり dp[i-1] ( i-1 位置で終わるすべての算術シーケンスの数)、
および abc は算術シーケンスに属し、dp[i-1] の場合には含まれないため、+1 が必要です
この場合: dp[i]=dp[i-1]+1
ケース 2: i i-1 i-2 位置要素が等差数列を構成しない
位置 i-2 の要素が a、位置 i-1 の要素が b、位置 i の要素が c であるとすると、これら 3 つの
差は異なります。つまり、cb と ba は等しくありません。
部分配列は連続しており、abc は等差数列を構成しないため、前の構造が等差数列を構成しないと意味がありません。この場合:
dp [i]=0
状態遷移方程式は次のとおりです:
dp[i]= cb==ba?dp[i-1]+1:0
完全なコード
class Solution {
public:
int numberOfArithmeticSlices(vector<int>& nums) {
int n=nums.size();
vector<int>dp(n,0);
int i=0;
int sum=0;
for(i=2;i<n;i++)
{
//状态转移方程
dp[i]=nums[i]-nums[i-1]==nums[i-1]-nums[i-2]?dp[i-1]+1:0;
sum+=dp[i];
}
//返回dp表中所有值之和
return sum;
}
};
等差数列には少なくとも 3 つの要素が必要なので、要素が 1 つまたは 2 つしかない場合は要件を満たさないため、dp[0]=0 dp[1]=0
978. 最長の乱流部分配列
クリックして表示:最長の乱流部分配列
整数配列 arr を指定すると、arr の最大の乱流部分配列の長さを返します。
サブ配列内の要素の隣接する各ペア間で比較符号が反転している場合、そのサブ配列は乱流サブ配列です。
より正式には、A[i]、A[i+1]、...、A[j] が次の条件のみを満たす場合、arr の部分配列を乱流部分配列と呼びます。 if i <= k < j : k が
の
場合奇数、A[k] > A[k+1]、
k が偶数の場合、A[k] < A[k+1]、
または i <= k < j の場合:
k が偶数の場合、A[ k] > A[k+1]、
k が奇数の場合、A[k] < A[k+1]。
例 1:
入力: arr = [9,4,2,10,7,8,8,1,9]
出力: 5
説明: arr[1] > arr[2] < arr[3] > arr[4] < arr[5]
例 2:
入力: arr = [4,8,12,16]
出力: 2
トピック分析
Bの値がAより大きい場合は上昇傾向、
Cの値がBより小さい場合は下降傾向、
Dの値がCより大きい場合は上昇傾向を示します。この場合、
ABCD 配列は乱流部分配列になります。
状態遷移方程式
dp[i]: 位置 i で終わるすべての部分配列の中で最も長い乱流配列の長さを示します。
dp[i]の解析と書き込みを始めたところですが、乱流配列に上昇傾向と下降傾向があり、dp[i]が解けないことがわかり、f[i]とg[i]を再定義します
f[i]: 位置 i で終わるすべてのサブ配列の中で最終的に上昇傾向を示す最長の乱流配列の長さを示します。
g[i]: 位置 i で終わるすべてのサブ配列の中で最終的に下降傾向を示す最長の乱流配列の長さを示します。
f[i] 状態遷移方程式
位置 i-1 の要素の値が a、位置 i の要素の値が b であるとします。
ケース 1 a>b
前の配列と組み合わせると、下降トレンドのみを表示できます
。上昇トレンドを自分で表示したい場合は、単一のサブ配列を作成できます。最後の位置は上昇または下降のいずれかになります。つまり、f[
i]=1
ケース 2 a<b
このとき、f[i]の意味に沿って上昇傾向にあります。
位置 i-1 で終了し、最終的に
下降傾向を示す乱流配列の最長の長さ ( g[i-1]に a から b までの長さ、つまり +1 ) を再度探します。: f[i]= g[i-1]+1
ケース 3 a==b
この場合、i の位置を上昇傾向にしたい場合は、1 つだけで部分配列を形成することしかできません。
つまり、f[i]=1 です。
g[i] 状態遷移方程式
位置 i-1 の要素の値が a、位置 i の要素の値が b であるとします。
ケース 1 a>b
現時点では、g[i]の意味と一致し、減少傾向を示しています。
位置 i-1 で終了し、最終的に上昇傾向を示す乱流配列の最長の長さを再度探します。つまり、 f[i-1] に a から
b までの長さを加えたもの、つまり +1 です。
この場合: g[i]=f[i-1]+1
ケース 2 a<b
この場合、i の位置を下降傾向にしたい場合は、1 つの部分配列、
つまり g[i]=1のみを形成できます。
ケース 3 a==b
この場合、i の位置を下降傾向にしたい場合は、1 つの部分配列、
つまり g[i]=1のみを形成できます。
完全なコード
class Solution {
public:
int maxTurbulenceSize(vector<int>& nums) {
int n=nums.size();
//f g表 都表示湍流数组的最长长度
//而单独一个数本身,表示最低长度为1
//所以f/g 最差长度也为1
vector<int>f(n,1);
vector<int>g(n,1);
int i=0;
//单独一个数 返回1 ,所以初始值为1
int fmax=1;
int gmax=1;
for(i=1;i<n;i++)
{
//f[i] a<b情况
if(nums[i-1]<nums[i])
{
f[i]=g[i-1]+1;
}
//g[i] a>b情况
else if(nums[i-1]>nums[i])
{
g[i]=f[i-1]+1;
}
fmax=max(fmax,f[i]);
gmax=max(gmax,g[i]);
}
//返回 f和g表中的最大值
return max(fmax,gmax);
}
};
単一の数値自体が上昇傾向または下降傾向を構成する可能性があるため、乱流配列の長さは最悪 1 となり、f/g テーブル内のすべての要素を
1 に初期化できます。
139. 単語の分割
クリックして表示:単語の分割
文字列 s と文字列のリスト wordDict を辞書として指定します。辞書に載っている単語の中からsをつなげられるかどうかを判断してください。
注: 辞書にあるすべての単語を使用する必要はなく、辞書にある単語は繰り返し使用できます。
例 1:
入力: s = “leetcode”, wordDict = [“leet”, “code”]
出力: true
説明: 「leetcode」は「leet」と「code」から連結できるため、true を返します。
例 2:
入力: s = “applepenapple”, wordDict = [“apple”, “pen”]
出力: true
説明: 「apple」「pen」「apple」から「applepenapple」を連結できるため true を返します。
辞書内の単語は再利用できることに注意してください。
例 3:
入力: s = “catsandog”, wordDict = [“cats”, “dog”, “sand”, “and”, “cat”] 出力
: false
状態遷移方程式
dp[i]: [0,i] 区間の文字列が辞書の単語と結合できるかどうかを示します。結合できる場合は
true を返し、結合できない場合は false を返します
最後の位置に応じて質問を分割します
前の部分が正常に結合できると判断でき、最後の単語が辞書に含まれることが保証されている場合は、文字列全体を結合できます。
j を最後の単語の開始位置とします。
添え字 j の範囲は 0<=j<=i です。
0 は最後の単語としての文字列全体を意味します。
i は最後の単語としての最後の文字を意味します。
文字列の開始位置は
最後の単語の開始位置として 0 j であるため、文字列の終了位置は j-1 になります。
区間 [0,j-1] の文字列は、辞書内の単語で結合できるかどうかを判断する必要があります。つまり、dp[j-1] の最後の単語の範囲は
[j,i] です。 ]、この区間の部分文字列は辞書にあります
状態遷移方程式は次のとおりです:
dp[i-1] が true で、最後の単語 ([ji] 内) が辞書内の両方を満たす場合、結果は true、どちらかが true でない場合は
false
初期化
j が 0 の場合、範囲外の問題が発生します
このような範囲外の問題を防ぐために、
展開された配列に仮想ノードが追加されます。仮想ノードの添字は 0 で、元の配列の要素の添字は 1 から始まります。
j が 0 の場合、0 から i までの範囲全体が最後の単語とみなされます。最後の単語が辞書にある場合は true が返されます。dp[0]=true は両方が true であることを保証し
ます
。
元の文字列が s であると仮定すると、補助位置は通常スペース
s=' ' +s
元の文字列に補助位置が追加されると、元の文字列は 1 から数えることと同じになり、新しい dp テーブルに対応します。
完全なコード
class Solution {
public:
bool wordBreak(string s, vector<string>& wordDict) {
unordered_set<string>hash;
for(auto& s:wordDict)
{
hash.insert(s);
}
int n=s.size();
//为了防止越界问题,所以多加一个虚拟节点
vector<bool>dp(n+1);
//初始化
dp[0]=true;
s=' '+s;//使原始字符串的下标统一+1
int i=0;
int j=0;
for(i=1;i<=n;i++)
{
for(j=i;j>=1;j--)
{
//在hash中判断 s中对应的子串在不在 若在返回1 不在返回0
if( dp[j-1]&& hash.count(s.substr(j,i-j+1)) )
{
dp[i]=true;
break;//找到一种情况即可
}
}
}
//因为多加了个虚拟节点,所以下标+1
return dp[n];
}
};