package main
import "slices"
func lengthOfLis(nums []int) int {
n := len(nums)
dp := make([]int, n) // dp[i] = length of LIS ending at nums[i]
for i := range dp {
dp[i] = 1
}
for i := 1; i < n; i++ {
for j := 0; j < i; j++ {
if nums[j] < nums[i] {
dp[i] = max(dp[i], dp[j]+1)
}
}
}
return slices.Max(dp)
}
// nums = [10, 9, 2, 5, 3, 7, 101, 18]
// dp = [1, 1, 1, 2, 2, 3, 4, 4]
// LIS: [2, 3, 7, 101] or [2, 5, 7, 101], length 4
static int LengthOfLis(int[] nums)
{
int n = nums.Length;
int[] dp = new int[n]; // dp[i] = length of LIS ending at nums[i]
Array.Fill(dp, 1);
for (int i = 1; i < n; i++)
{
for (int j = 0; j < i; j++)
{
if (nums[j] < nums[i])
{
dp[i] = Math.Max(dp[i], dp[j] + 1);
}
}
}
return dp.Max();
}
// nums = [10, 9, 2, 5, 3, 7, 101, 18]
// dp = [1, 1, 1, 2, 2, 3, 4, 4]
// LIS: [2, 3, 7, 101] or [2, 5, 7, 101], length 4
def length_of_lis(nums: list[int]) -> int:
n = len(nums)
dp = [1] * n # dp[i] = length of LIS ending at nums[i]
for i in range(1, n):
for j in range(i):
if nums[j] < nums[i]:
dp[i] = max(dp[i], dp[j] + 1)
return max(dp)
# nums = [10, 9, 2, 5, 3, 7, 101, 18]
# dp = [1, 1, 1, 2, 2, 3, 4, 4]
# LIS: [2, 3, 7, 101] or [2, 5, 7, 101], length 4
O(n log n) версия с бинарным поиском:
<?php
declare(strict_types=1);
/**
* O(n log n) version with binary search.
*
* @param list<int> $nums
*/
function lengthOfLisOptimized(array $nums): int
{
$tails = []; // tails[i] = smallest tail of LIS of length i+1
foreach ($nums as $num) {
$lo = 0;
$hi = count($tails);
// Binary search for leftmost position >= num
while ($lo < $hi) {
$mid = $lo + intdiv($hi - $lo, 2);
if ($tails[$mid] < $num) {
$lo = $mid + 1;
} else {
$hi = $mid;
}
}
if ($lo === count($tails)) {
$tails[] = $num;
} else {
$tails[$lo] = $num;
}
}
return count($tails);
}
package main
import "sort"
// O(n log n) version with binary search.
func lengthOfLisOptimized(nums []int) int {
var tails []int // tails[i] = smallest tail of LIS of length i+1
for _, num := range nums {
// Binary search for leftmost position >= num
pos := sort.SearchInts(tails, num)
if pos == len(tails) {
tails = append(tails, num)
} else {
tails[pos] = num
}
}
return len(tails)
}
// O(n log n) version with binary search.
static int LengthOfLisOptimized(int[] nums)
{
List<int> tails = []; // tails[i] = smallest tail of LIS of length i+1
foreach (int num in nums)
{
// BinarySearch returns the bitwise complement of the insertion point
// when the value is absent, which is the leftmost position >= num
int pos = tails.BinarySearch(num);
if (pos < 0)
{
pos = ~pos;
}
if (pos == tails.Count)
{
tails.Add(num);
}
else
{
tails[pos] = num;
}
}
return tails.Count;
}
from bisect import bisect_left
def length_of_lis_optimized(nums: list[int]) -> int:
"""O(n log n) version with binary search."""
tails: list[int] = [] # tails[i] = smallest tail of LIS of length i+1
for num in nums:
# Binary search for leftmost position >= num
pos = bisect_left(tails, num)
if pos == len(tails):
tails.append(num)
else:
tails[pos] = num
return len(tails)
## Задача 4: Unique Paths
Условие: робот в левом верхнем углу сетки m x n. Может идти только вправо или вниз. Количество путей до правого нижнего угла.
package main
func uniquePaths(m, n int) int {
dp := make([][]int, m)
for i := range dp {
dp[i] = make([]int, n)
for j := range dp[i] {
dp[i][j] = 1
}
}
for i := 1; i < m; i++ {
for j := 1; j < n; j++ {
dp[i][j] = dp[i-1][j] + dp[i][j-1]
}
}
return dp[m-1][n-1]
}
// m=3, n=3:
// [[1, 1, 1],
// [1, 2, 3],
// [1, 3, 6]]
// Answer: 6
static int UniquePaths(int m, int n)
{
// Rectangular 2D array fits the grid DP table naturally
int[,] dp = new int[m, n];
for (int i = 0; i < m; i++)
{
dp[i, 0] = 1;
}
for (int j = 0; j < n; j++)
{
dp[0, j] = 1;
}
for (int i = 1; i < m; i++)
{
for (int j = 1; j < n; j++)
{
dp[i, j] = dp[i - 1, j] + dp[i, j - 1];
}
}
return dp[m - 1, n - 1];
}
// m=3, n=3:
// [[1, 1, 1],
// [1, 2, 3],
// [1, 3, 6]]
// Answer: 6
def unique_paths(m: int, n: int) -> int:
# Comprehension builds the table pre-filled with the base case
dp = [[1] * n for _ in range(m)]
for i in range(1, m):
for j in range(1, n):
dp[i][j] = dp[i - 1][j] + dp[i][j - 1]
return dp[m - 1][n - 1]
# m=3, n=3:
# [[1, 1, 1],
# [1, 2, 3],
# [1, 3, 6]]
# Answer: 6
Оптимизация до O(n) памяти:
<?php
declare(strict_types=1);
function uniquePathsOptimized(int $m, int $n): int
{
$row = array_fill(0, $n, 1);
for ($i = 1; $i < $m; $i++) {
for ($j = 1; $j < $n; $j++) {
$row[$j] += $row[$j - 1];
}
}
return $row[$n - 1];
}
package main
func uniquePathsOptimized(m, n int) int {
row := make([]int, n)
for j := range row {
row[j] = 1
}
for i := 1; i < m; i++ {
for j := 1; j < n; j++ {
row[j] += row[j-1]
}
}
return row[n-1]
}
static int UniquePathsOptimized(int m, int n)
{
int[] row = new int[n];
Array.Fill(row, 1);
for (int i = 1; i < m; i++)
{
for (int j = 1; j < n; j++)
{
row[j] += row[j - 1];
}
}
return row[n - 1];
}
def unique_paths_optimized(m: int, n: int) -> int:
row = [1] * n
for _ in range(1, m):
for j in range(1, n):
row[j] += row[j - 1]
return row[n - 1]
## Задача 5: Maximum Subarray (Kadane's Algorithm)
<?php
declare(strict_types=1);
/**
* @param list<string> $wordDict
*/
function wordBreak(string $s, array $wordDict): bool
{
$wordSet = array_flip($wordDict);
$n = strlen($s);
$dp = array_fill(0, $n + 1, false);
$dp[0] = true; // Empty string can be segmented
for ($i = 1; $i <= $n; $i++) {
for ($j = 0; $j < $i; $j++) {
if ($dp[$j] && isset($wordSet[substr($s, $j, $i - $j)])) {
$dp[$i] = true;
break;
}
}
}
return $dp[$n];
}
// s = "leetcode", wordDict = ["leet", "code"]
// dp[0]=T
// dp[4]=T (s[0:4]="leet" in dict)
// dp[8]=T (dp[4]=T and s[4:8]="code" in dict)
// Answer: true
package main
func wordBreak(s string, wordDict []string) bool {
wordSet := make(map[string]bool, len(wordDict))
for _, w := range wordDict {
wordSet[w] = true
}
n := len(s)
dp := make([]bool, n+1)
dp[0] = true // Empty string can be segmented
for i := 1; i <= n; i++ {
for j := 0; j < i; j++ {
if dp[j] && wordSet[s[j:i]] {
dp[i] = true
break
}
}
}
return dp[n]
}
// s = "leetcode", wordDict = ["leet", "code"]
// dp[0]=T
// dp[4]=T (s[0:4]="leet" in dict)
// dp[8]=T (dp[4]=T and s[4:8]="code" in dict)
// Answer: true
static bool WordBreak(string s, IEnumerable<string> wordDict)
{
HashSet<string> wordSet = [.. wordDict];
int n = s.Length;
bool[] dp = new bool[n + 1];
dp[0] = true; // Empty string can be segmented
for (int i = 1; i <= n; i++)
{
for (int j = 0; j < i; j++)
{
if (dp[j] && wordSet.Contains(s[j..i]))
{
dp[i] = true;
break;
}
}
}
return dp[n];
}
// s = "leetcode", wordDict = ["leet", "code"]
// dp[0]=T
// dp[4]=T (s[0:4]="leet" in dict)
// dp[8]=T (dp[4]=T and s[4:8]="code" in dict)
// Answer: true
def word_break(s: str, word_dict: list[str]) -> bool:
word_set = set(word_dict)
n = len(s)
dp = [False] * (n + 1)
dp[0] = True # Empty string can be segmented
for i in range(1, n + 1):
for j in range(i):
if dp[j] and s[j:i] in word_set:
dp[i] = True
break
return dp[n]
# s = "leetcode", word_dict = ["leet", "code"]
# dp[0]=T
# dp[4]=T (s[0:4]="leet" in dict)
# dp[8]=T (dp[4]=T and s[4:8]="code" in dict)
# Answer: True