目次
サブシーケンスの増加に関連するいくつかのトピックがあります。いくつかのトピックには最適なソリューションがありますが、ここでの主な焦点は動的計画法の実践であるため、動的計画法またはいくつかの一般的な方法のみが記述されています。
これらのトピックを紹介する前に、まずサブシーケンスとは何かとサブストリングとは何かを区別します
サブシーケンス:シーケンス[4, 6, 5]
が[1, 2, 4, 3, 7, 6, 5]
サブシーケンスであるなど、サブシーケンスは 連続的ではありません
部分文字列:部分文字列は連続しています
1. 最長の連続増加シーケンス
入力:[1,3,5,4,7]
出力:3
説明:最長の連続増加シーケンスは[1,3,5]であり、長さは3です。
[1,3,5,7]も昇順のサブシーケンスですが、元の配列では5と7が4で区切られているため、連続ではありません。
この質問はシーケンスの増加に関するものですが、要件は継続的であるため、実際には部分文字列の増加を求めています
class Solution:
def findLengthOfLCIS(self, nums: List[int]) -> int:
if not nums:
return 0
if len(nums) == 1:
return 1
dp = [1]*len(nums) #代表连续递增序列的长度
for i in range(1,len(nums)):
if nums[i]>nums[i-1]: #如果后一个元素大于前一个元素,那长度+1
dp[i] = dp[i-1]+1
return max(dp) # 找出最大的那个数就是长度,不能是dp[-1],dp[-1]代表的是以最后一个元素结尾是最长连续递增序列,但是最后一个并不一定是最大的数。
2. 最長の昇順サブシーケンス
動的計画法を使用してそれを行い、サブシーケンスの長さを表すようにdp配列を定義し、dp [i]は開始点としてi番目の要素の前の任意の要素を表し、iの終わりまでの昇順のサブシーケンスの長さを表します-番目の要素。1に初期化されます。これは、各要素が少なくとも個別にサブシーケンスになることができ、この時点で長さが1であることを意味します。
各要素nums [i]に移動した後、[0-i]の要素nums [j]を最初から移動します。nums[i]> nums [j]の場合、nums [i]にアクセスできます。 nums [j]要素、つまりdp [i] = dp [j] + 1
したがって、再帰方程式はdp [i] = max(dp [i]、dp [j] +1)であり、最終的には、dp [-1]ではなくmax(dp)が見つかります。
class Solution:
def lengthOfLIS(self, nums: List[int]) -> int:
if not nums:
return 0
dp=[1]*len(nums)
for i in range(len(nums)):
for j in range(i):
if nums[i]>nums[j]:
dp[i] = max(dp[i],dp[j]+1)
return max(dp)
この質問の内側のループが毎回0からトラバースする場合、実際には何度もトラバースが繰り返されるため、内側のループは二分法を使用してnum [i]より大きい最初の数値を見つけ、それをnum [i]に置き換えることができます。言うまでもありませんが、コードは次のとおりです。
class Solution:
def lengthOfLIS(self, nums: List[int]) -> int:
res = []
for num in nums:
if not res or num>res[-1]:
res.append(num)
else:
l=0
r=len(res)-1
while l<r:
mid = (l+r)//2
if res[mid]<num:
l = mid+1
else:
r = mid
res[l]=num
return len(res)
3.最長増加部分列の数
この質問は、最も長く増加するサブシーケンスの数の追加カウントがあることを除いて、上記の質問と非常に似ています。
class Solution:
def findNumberOfLIS(self, nums: List[int]) -> int:
if not nums:
return 0
dp = [1]*len(nums)
count = [1]*len(nums)
temp = 1
res = 0
for i in range(1,len(nums)):
for j in range(i):
if nums[i]>nums[j]:
if dp[j]+1>dp[i]:
dp[i] = dp[j]+1
count[i] = count[j]
elif dp[i] == dp[j]+1:
count[i]+=count[j]
# temp = max(temp,dp[i])
temp = max(dp)
for i in range(len(nums)):
if dp[i] == temp:
res += count[i]
return res
dp [j] +1> dp [i]の場合、新しい長さが初めて検出されたことを意味します。つまり、dp [j] +1です。このとき、count [i] = count [j]であり、 nums[i]
最長の終わり増加するサブシーケンスの組み合わせは、nums[j]
現在の組み合わせと同じです
dp [j] +1 = dp [i]の場合、この長さの増加するシーケンスが1回検出されたことを意味し、 count[i]+=count[j],
既存の組み合わせの数とカウント[j]の合計が組み合わせの総数になります。
最後のトラバーサルは最長の長さを見つけることであり、それに対応するカウントが結果になります
この質問にはツリー配列を使用することもできます。現在、機能が制限されているため、ツリー配列の使用方法がわかりません。スキップ
4.サブシーケンスの増加
この問題は、動的計画法ではなく、バックトラッキングと同様の深さ優先探索を使用しています。一般的なバックトラッキングソリューションを使用すると、時間がかかりすぎます。コードは次のとおりです。
class Solution:
def __init__(self):
self.res = []
def findSubsequences(self, nums: List[int]) -> List[List[int]]:
def dfs(temp,start,nums):
if len(temp)>=2 and temp not in self.res:
self.res.append(temp[:])
for i in range(start,len(nums)):
if not temp or nums[i]>=temp[-1]:
temp.append(nums[i])
dfs(temp,i+1,nums)
temp.pop()
temp = []
dfs(temp,0,nums)
return self.res
コレクションを使用してプルーニングするのに6,000ミリ秒以上かかることがわかりますが、それでも時間がかかりすぎます。辞書を使用してプルーニングすることができます。
class Solution:
def findSubsequences(self, nums: List[int]) -> List[List[int]]:
res = []
def dfs(start, tmp):
dic = {}
if len(tmp) > 1:
res.append(tmp)
for i in range(start, len(nums)):
if dic.get(nums[i], 0):
continue
if len(tmp) == 0 or nums[i] >= tmp[-1]:
dic[nums[i]] = 1
dfs(i + 1, tmp + [nums[i]])
dfs(0, [])
return res
時間のかかるものが突然落ちました。
5.最長の連続シーケンス
最初は空のままにし、コレクションを学習して確認してから、改善します