日常刷题笔记。

  1. 最长公共子串

给定两个字符串A和B,长度分别为m和n,要求找出它们最长的公共子串,并返回其长度。例如:

1
2
A = "HelloWorld"
B = "loop"

第一种做法是暴力枚举, 时间复杂度是 $O(n^3)$; 第二种做法时间复杂度 $O(n^2)$,使用dp 的做法。那么状态转移方程是

$$ dp[i][j] = \begin{cases} 0 & str1[i-1] !=str2[j-1] \\
dp[i-1][j-1] +1& str1[i-1] ==str2[j-1] \end{cases}$$

注意公共子串,要求的是需要连续的,所以当发现不连续的时候, $dp[i][j] =0$ 应该是这样的考虑。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
#include<iostream>
#include<string>
#include<vector>
using namespace std;

// 这个是最基础的算法, 使用暴力枚举,时间复杂度是 n^3
string common_str(string &str1, string & str2)
{
    string res;
    int n =str1.size(), m =str2.size();
    //保证n>= m
    if(n< m)
    {
        res =common_str(str2, str1);
        return res;
    }
    int max_l =0;
    for(int i=0; i<n; i++)
    {
        for(int j =i+1; j<n; j++)
        {
            // start point , len
            string substr= str1.substr(i,j-i+1);
            if( str2.find(substr) != str2.npos)
            {
                if(j -i+1 > max_l)
                {
                    res =substr;
                    max_l =j-i+1;
                }
            }
        }
    }
    return res;
}

// 使用二维dp 进行优化,时间复杂度是O(n^2) 空间是O(n^2)
int common_str2(string &str1, string &str2)
{
    int n =str1.size(), m =str2.size() ;
    vector<vector<int>> dp(n+1, vector<int>(m+1, 0));
    if(n <m) return common_str2(str2, str1);
    int max_l =0;
    for(int i =1; i<=n ; i++)
    {
        for(int j =1; j<=m ; j++)
        {
            if(str1[i-1]  ==str2[j-1])
            {
                dp[i][j] =dp[i-1][j-1] +1;
                max_l =max(max_l, dp[i][j]);
            }
            else
                dp[i][j] =0;
        }
    }
    // 这个是dp 思想求解,但是最长的长度不是dp[n][m]
    return max_l;
}
// 使用一维dp 进行优化
int findLongest(string A, int n, string B, int m) {
    if(n == 0 || m == 0)
        return 0;
    int rs = 0;
    int dp[n + 1][m + 1];
    for(int i = 0 ; i <= n; i++)//初始状态
        dp[i][0] = 0;
    for(int i = 0; i <= m; i++)
        dp[0][i] = 0;
    for(int i = 1; i <= n; i++)
        for(int j = 1; j<= m; j++)
        {
            if(A[i - 1] == B[j - 1])
            {
                dp[i][j] = dp[i -1][j - 1] + 1;
                rs = max(rs,dp[i][j]);//每次更新记录最大值
            }
            
            else//不相等的情况
                dp[i][j] = 0;
        }
    return rs;//返回的结果为rs
}

int main()
{
    string str1, str2;
    //cin >>str1 >>str2;
    str1 ="abc", str2 ="bcd";
    //string res =common_str(str1, str2);
    //for(auto u : res) cout << u;
    int max_l =common_str2(str1, str2);
    cout << max_l<< endl;
    //cout << findLongest(str1, 3, str2, 3)<< endl;
    cout <<endl;
    return 0;
}

python 解法

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
def longest_substring(str1, str2):
    n, m =len(str1), len(str2)
    dp=[[0] *m]*n
    max_ =0
    res =""
    for i in range(n):
        for j in range(m):
            if str1[i] ==str2[j]:
                if i and j:
                    dp[i][j] =dp[i-1][j-1] +1
                else:
                    dp[i][j] =1
                if dp[i][j] >max_:
                    max_ =dp[i][j]
                    res += str1[i]
    return res
str1 ="abc"
str2 ="bcd"
print(longest_substring(str1, str2))
  1. 求解最长公共子序列

dp[i][j] 表示string1中第 i 位置之前和string2 中第j 位置之前,最长的公共子序列。(求解的是max 操作,对于集合的划分可以重复,但是不能缺少)

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
#include<iostream>
#include<string>
#include<vector>
using namespace std;
int max_sub_len(string & str1, string &str2)
{
    int res =0;
    int n =str1.size(), m =str2.size();
    vector<vector<int>> dp(n+1, vector<int>(m+1, 0));
    // 坐标的转换
    // 如果是string,这个坐标是可以从
    for(int i =0; i<n; i++)
    {
        for(int j =0; j<m; j++)
        {
            dp[i+1][j+1] =max(dp[i][j+1], dp[i+1][j]);
            if(str1[i] ==str2[j])
                dp[i+1][j+1] =max(dp[i+1][j+1], dp[i][j] +1);
        }
    }
    return dp[n][m];
}
int main()
{
    string str1, str2;
    cin >> str1 >> str2;
    int res =max_sub_len(str1, str2);
    cout << res<< endl;
    return 0;
}

python 解法

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
# 最长公共子序列,这个可以是离散的
def longest_common_string(str1, str2):
    n, m =len(str1), len(str2)
    dp =[[0]* (m+1)]*(n+1)
    res =""
    for i in range(1, n+1):
        for j in range(1, m+1):
            dp[i][j] =max(dp[i-1][j], dp[i][j-1])
            if str1[i-1] ==str2[j-1]:
                if dp[i][j]< dp[i-1][j-1] +1:
                    res +=str1[i-1]
                    dp[i][j] = dp[i-1][j-1] +1
    # dp[m][n] 是最大值    
    return res
str1 ="abc"
str2 ="adc"
print(longest_common_string(str1, str2))
  1. 最长上升子序列

最长公共上升子序列

暴力的解法是 $O(n^2)$,就是枚举当前位置之前的数字是否合法,如果合法,那么就需要 +1 操作。对于dp 动态规划,含义,初始化和动态转移方程。dp[i] 表示位置为i 的所有的前面的数字有多少个上升序列数字。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
class Solution {
public:
    // 最长递增子序列,动规的角度去考虑, 枚举法, 时间复杂度是O(n^2)
    int lengthOfLIS(vector<int>& nums) {
        int n =nums.size();
        if(n ==0) return 0;
        // 遍历当前位置之前的所有的位置,统计出现的小于当前数值的数字,就可以得到最后的结果
        int res =1;
        vector<int> dp(n, 1);

        for( int i =1; i<n; i++)
        {
            // 这个是保证之前的所有位置的数字
            for(int j =0; j<i; j++)
            {
                if(nums[i]> nums[j])   dp[i] =max(dp[i], dp[j]+1);              
            }
            res =max(res, dp[i]); 
        }
        for(auto u : dp) cout << u<<" ";
        cout << endl;
        return res;
    }
};

使用二分进行优化,空间上的复杂度不变,但是时间上的复杂度由原先的 $O(n^2)$ 优化成 $O(nlogn)$,这个样子。使用数组存储的是有序的数字,那么最后数组的长度就是最长上升序列的长度。

一旦有序之后,那么就可以使用二分等思想进行

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
class Solution {
public:
    int lengthOfLIS(vector<int>& nums) {
        // 使用二分查找进行了优化 时间复杂度从O(n^2) 优化成了 O(nlogn)
        int n =nums.size();
        if(n ==0) return 0;
        vector<int> h(n+1, 0);
        h[1] =nums[0];
        int max_ =1;
        for(int i =1; i< n; i++)
        {
            int l =1, r =max_;
            while(l<=r)
            {
                int mid =l +r >>1;
                if(h[mid] < nums[i]) l =mid +1;
                else r =mid -1;
            }
            h[l] =nums[i];
            if(l > max_) max_ =l;
        }
        return max_;   
    }
};

python 解法

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
class Solution(object):
    def lengthOfLIS(self, nums):
        """
        :type nums: List[int]
        :rtype: int
        """
        # 最长上升子序列,暴力解法, n^2 的时间复杂度, 然后怎么优化呢? 因为这个是有序的,所以可以考虑使用 二分进行优化
        n =len(nums)
        if(n ==0): return 0;
        h =[0] *(n+1);
        h[1] =nums[0]
        max_ =1
        for i in range(1, n):    
            l, r = 1, max_
            while(l <=r ):
                mid =l +r >>1;
                if(h[mid] < nums[i]): l =mid +1
                else: r =mid-1
            h[l] =nums[i]
            if(l > max_):
                max_ =l
        return max_
  1. 将嵌套列表转换成一维列表
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
res =[]
def flatten(items):
    for item in items:
        if isinstance(item, int) or isinstance(item, str):
            #flatten(item)
            res.append(item)
        else:
            flatten(item)
            #res.append(item)
    return res
items =[1,2 , 'a', [3, 4, ], (5, 6)]
print(flatten(items))

其实还可以写成一个小的生成器的形式

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
def generator(a):
    for each in a:
        if not isinstance(each, list):
            yield each
        else:
            yield from generator(each)
a =[1,2, [3, 4], 5]
print(generator(a))

def generator1(a):
    for each in a:
        if isinstance(each, collections.Iterable) :
            yield from generator1(each)
        else:
            yield each
print(generator1(a))
  1. remove linked list ellements

c++ 版本,主要想要个强调,如果是删除一个元素,那么需要处理f 删除头结点的问题,所以使用一个 dummy 这样的虚拟头结点,可以处理边界条件。对于有可能删除头结点的情况下,一般使用 dummy 这种虚拟头结点

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode(int x) : val(x), next(NULL) {} //  这个属于显式构造函数
 * };
 */
class Solution {
public:
    ListNode* removeElements(ListNode* head, int val) {
        ListNode * dummy = new ListNode(-1);
        ListNode * p =dummy;
        p ->next =head;
        while(p->next)
        {
            auto nex =p->next;
            if(nex->val == val)
            {
                p->next = nex->next;
            }
            else
                p =p->next;
        }
        return dummy->next;   
    }
};

python 版本代码

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
# definition of listnode
class ListNode:
	def __init__(self, x):
		self.val =x
		self.next =None
		
class Solution:
    def removeElements(self, head: ListNode, val: int) -> ListNode:
        dummy = ListNode(-1)
        p =dummy
        p.next =head
        while p.next:
            nex =p.next
            if(val  == nex.val):
                p.next =nex.next
            else:
                p= p.next
        return dummy.next
  1. 74. Search a 2D Matrix

python 解法

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
class Solution:
    def searchMatrix(self, matrix: List[List[int]], target: int) -> bool:
        if not matrix: return False
        n =len(matrix)
        if n ==0: return false
        m =len(matrix[0])
        i, j =0, m -1
        while i< n and j >=0:
            if(matrix[i][j] == target): return True
            elif matrix[i][j] > target: j -=1
            else : i +=1
        return False

c++ 解法是类似的,就不写了

  1. 统计中英文字符的个数 和 统计英文单词并按照倒序排列

关于统计,提供了常规做法和正则匹配两种方法

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
import string
str1 ="找出字符串中的中英文、i only regret空格、数字、标点符号 1 99 0个数"
def count_(str):
    cout_num =cout_zh =cout_en =cout_space =cout_pu =0
    for ch in str:
        if ch.isspace():
            cout_space +=1
        elif ch.isdigit():
            cout_num +=1
        elif ch in string.ascii_letters:
            cout_en +=1
        elif ch.isalpha(): # 这里使用的是减法的思路
            cout_zh +=1
        else:
            cout_pu +=1
    print("英文字符数量:", cout_en)
    print("中文字符数量:", cout_zh)
    print("数字个数:", cout_num)
    print("空格个数: ", cout_space)
    print("特殊字符个数:",cout_pu)

#count_(str1)

# 方法二,使用正则表达式
import re
def cout2(str):
    regex_digit = re.compile(r'[0-9]' )
    regex_en =re.compile(r'[a-zA-Z]')
    regex_zh =re.compile(r'[\u4E00-\u9FA5]')

    digit_list =regex_digit.findall(str)
    en_list =regex_en.findall(str)
    zh_list =regex_zh.findall(str)

    print("数字序列", digit_list)
    print("英文序列", en_list)
    print("中文序列", zh_list)

cout2(str1)
  1. 统计的单词的个数(注意不是字母 char类型的), 有不同的方法

a. 在linux 环境下 文件格式:文件中每一行为一个单词

1
2
3
4
sort filename | uniq -c| sort -nr
-c 输出重复次数 
-n 按照数值比较排序
-r 逆序输出结果

b. 全程python 操作

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
import sys
word2count ={}
def word_count():
    f1 =open("", 'r')
    f2 =open("", 'w')
    while True:
        line =f1.readline()
        if not line: break
        # python 内置的split 只能使用单个 分割符
        line =line.replace(',', ' ')
        line =line.replace('.', ' ')
        line =line.replace('!', ' ')
        line =line.strip().split(" ")
        
        # 可以使用 re模块中的 split() ,使用多个分割符对句子进行分割
        import re
        line =re.split(',.?', line)
        
        for item in line:
            if item not in word2count:
                word2count[item] =1
            else:
                word2count[item] +=1
    # 按照逆序进行排序
    res =sorted(word2count.item(), key =lambda x : x[1], reverse =True)
    for item in res:
        f2.write(item[0])
    
    f1.close()
    f2.close()

python 中常见的大小写问题

1
2
3
4
print(str.upper())          # 把所有字符中的小写字母转换成大写字母
print(str.lower())          # 把所有字符中的大写字母转换成小写字母
print(str.capitalize())     # 把第一个字母转化为大写字母,其余小写
print(str.title())          # 把每个单词的第一个字母转化为大写,其余小写 
  1. 最长01相同子串 已知一个长度为N的字符串,只由0和1组成, 求一个最长的子串,要求该子串出现0和1的次数相等。 思路: 最简单的方式是先生成字串,然后判断每个字串是否满足0的个数和1的个数相同。这种暴力求解时间复杂度O(n^3),明显是不合理的。 下面说一下简单的做法: 定义一个数组B[N],B[i]表示从A[0…i]中 num_of_0 - num_of_1,0的个数与1的个数的差 。那么如果A[i] ~ A[j]是符合条件的子串,一定有 B[i] == B[j],因为中间的部分0、1个数相等,相减等于0。

时间复杂度:O(n),空间复杂度:O(n)

注意这个是求解的长度,是一个最值问题,而不是最长的01 子串本身。所以使用临时数组记录目前为止的01 的差值,然后选择最大的就行了。时间复杂度是O(N),空间复杂度是O(N)。

算法的优化的关键在于,可以基于前一步的计算结果继续往下计算。前一步算出了 (i-1) 的01 的差值,那么下一步可以计算前 i 的01 的差值。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
def lengest01SubStr(s):
    '''
    最长0,1 相等的子串长度
    '''
    count =[0, 0]
    B =[0]*len(s)
    dic ={} # 保存 0 1 的差值
    lengest =0

    for i in range(len(s)):
        count[int(s[i])]  +=1
        B[i]  =count[0] - count[1]
        # start from 0th index
        if B[i] ==0:
            lengest +=1
            continue
            
        if B[i] in dic:
        # i -dic[B[i]] , not from 0th index
            lengest =max(lengest, i- dic[B[i]])
        else:
            dic[B[i]] =i
    return lengest

a ='1011010'
b ='10110100'
print(lengest01SubStr(a)) # 6 # '011010'
print(lengest01SubStr(b)) # 8 # '10110100'

** 顺时针打印矩阵**

输入一个矩阵,按照从外向里以顺时针的顺序依次扫印出每一个数字。

使用四个循环,就可以解决,这个应该属于找规律的题目。

思路: 找到左上角的,一个start_point, 然后根据这个点进行上下左右的循环。

python 中的 range() 是一种左闭右开的区间,并且在逆序遍历的时候,区间的值是不变的,最后使用 -1 就OK了。还有遍历时候的 i, j 都只是一种index,如果一个够用的话,那么就没有必要使用两个。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
def printMatrix(matrix):
    if not matrix or matrix ==[[]]:
        return
        # 第一次见这样判断空的matrix
    row =len(matrix)
    column =len(matrix[0])
    # 这里的left, right, up, down 都是真实能够access到数据的

    left =0
    right =column -1
    up =0
    down =row -1
    res =[]
    while left <right and up <down:
        # from left to right
        for i in range(left, right+1): 
            res.append(matrix[up][i])
        # from up to down
        for i in range(up+1, down+1):
            res.append(matrix[i][right])
        # from right to left
        for i in range(right-1, left-1, -1): 
            res.append(matrix[down][i])
        for i in range(down-1, up, -1):
            res.append(matrix[i][left])
        left +=1
        right -=1
        up +=1
        down -=1
    # 最后对于这种特殊情况的处理是容易忘记的
    # left one row 这种情况很特殊,只是从左往右遍历
    if up ==down and left <right:
        for i in range(left, right+1):
            res.append(matrix[up][i])
    # left one column 只有可能是从上往下遍历
    if left ==right and up <down:
        for i in range(up, down+1):
            res.append(matrix[i][left])
    if up ==down and left ==right:
        res.append(matrix[left][up])
    return res
print(printMatrix(matrix))

** 最小调整有序**

有一个整数数组,请编写一个函数,找出索引m和n,只要将m和n之间的元素排好序,整个数组就是有序的。注意:n-m应该越小越好,也就是说,找出符合条件的最短序列。 给定一个int数组A和数组的大小n,请返回一个二元组,代表所求序列的起点和终点。(原序列位置从0开始标号,若原序列有序,返回[0,0])。保证A中元素均为正整数。

样例

1
2
[1,4,6,5,9,10],6
返回:[2,3]

第一种比较暴力,时间复杂度 $O(nlogn)$

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
/*
 最小调整有序
 找出索引m 和n,只要是m 和n 之间的元素排好序,整个数组就是有序的。注意n-m 应该是越小越好,找出符合条件的最短序列
 */
#include<iostream>
#include<vector>
using namespace std;
vector<int> find_segment(vector<int>& arr)
{
    vector<int> res(2, 0);
    vector<int> tmp(arr.begin(), arr.end());
    sort(arr.begin(), arr.end());
    for(int i =0; i< arr.size(); i++)
    {
        if(tmp[i] != arr[i])
        {
            res[0] =i;
            break;
        }
    }
    for(int i =arr.size() -1; i>=0; i--)
    {
        if(tmp[i] != arr[i])
        {
            res[1] =i;
            return res;
        }
    }
    return res;
}

int main()
{
    int n;
    vector<int> arr;
    cin >>n;
    for(int i =0; i< n; i++)
    {
        int tmp;
        cin >> tmp;
        arr.push_back(tmp);
    }
    vector<int> res =find_segment(arr);
    for(auto u: res)
        cout << u << " ";
    cout << endl;
    return 0;
}

第二种是$O(n)$ 的时间复杂度。分成两个步骤

  • 从左往右找,如果当前元素小于之前的最大元素则说明当前元素应处于A[first,last]无序序列中而且当前元素是当前最大下标的无序元素。
  • 从右往左找,如果当前元素大于之前的最小元素则说明当前元素应处于A[first,last]无序序列中而且当前元素是当前最小下标的无序元素。

关键在于找到之后,继续遍历直到数组的最后;

增加新的一个样例

1
对于一个整数数组[1,3,5,8,4,10,12,9,15,17],则最短无序数组为[5,8,4,10,12,9]。

好好思考一下,当时为什么想不到,这种思路是什么,我当时是想到了 $O(n)$ 遍历,但是没有相当即使第一回发现了不符合要求的数字,那么也是要遍历完,然后不断地的更新结果的。

开始的想法是正确的,左右两次遍历。但是需要不断的更新最值,从右往左是最小值,从左往右是最大值。不能只是和周围的数字进行比较。

简而言之就是找出,逆序的, 因为是有序的,所以如果发现一个比在之前数字小,那么说明找到了右端点;从右向左,如果发现比左边大,说明找到了左端点;需要转换一下思路

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
class Rearrange {
public:

    vector<int> findSegment(vector<int> nums, int n) {
        // write code here
        int maxv =nums[0], minv =nums[n-1];
        vector<int> res(2, 0);
        
        for(int i =0; i< n; i++)
        {
            if(nums[i] >= maxv) maxv =nums[i];
            else  res[1] =i;
        }
        
        for(int i =n -1; i>=0; i--)
        {
            if(nums[i] <= minv )minv =nums[i];
            else res[0] =i;
        }
        return res;
    }
};

python 实现的代码

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
# -*- coding:utf-8 -*-
class Rearrange:
    def findSegment(self, nums, n):
        # write code here
        if n==1: return [0, 0]
        minv, maxv =nums[-1], nums[0]
        res =[0, 0]
        for i in range(n):
            if nums[i] >= maxv: maxv =nums[i]
            else: res[1] =i
        
        for i in range(n-1, -1, -1):
            if nums[i] <=minv: minv =nums[i]
            else: res[0] =i
        return res        

915. Partition Array into Disjoint Intervals

考察想法的题目真的是太难了,如果想不出来,那么真的是不会做。 分成左右两个部分,左边的全部比右边的小。那么只要保证左边最大的是小于等于右边最小的就行。这个思路是,数学题。下面就需要使用一个 $O(n)$ 空间复杂度记录从 $i$ 到$n$ 的最小值。不用使用数组保存前 $i$ 的最大值,直接在遍历的时候进行判断进行了。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
class Solution {
public:
    // tong
    // 因为要求分成两部分,左边的都比右边的小,
    // 使用一个数组 tmp[i] 表示从 i 到 n-1 最大值
    // 然后经过一次遍历之后,如果某个arr[i] < tmp[i] 那么就得到了正确的解
    int partitionDisjoint(vector<int>& nums) {
        int n =nums.size();
        vector<int> minv(n, INT_MAX);
        minv[n-1] =nums[n-1];
        for(int i =n-2; i>=0; i--)
        {
            minv[i] =min(minv[i+1], nums[i] );
            //cout << maxv[i] << endl;
        } 
        // 这里还需要记录到到当前位置的最大值 
        int maxv =0;
        for(int i =0 ; i<n-1; i++)
        {
            maxv= max(maxv, nums[i]);
            if (maxv <=minv[i+1])
                return i +1;
        }
        return -1;
    }
};

python 实现

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
import sys
class Solution(object):
    def partitionDisjoint(self, A):
        """
        :type A: List[int]
        :rtype: int
        """
        n =len(A)
        minv=[sys.maxsize]*n
        minv[n-1] =A[n-1]
        for i in range(n-2, -1, -1):
            minv[i] =min(minv[i+1], A[i])
        #print(minv)
        maxv =0
        for i in range(n-1):
            maxv=max(maxv, A[i])
            if(maxv <= minv[i+1]):
                return i +1
        return -1

149. Max Points on a Line

这个题目需要处理竖直直线,重复的点等特殊情况。剩下的一般情况是需要使用 顶点+ 斜率进行表示,使用dictionary 进行表示。 注意在python 中使用的是 gcb 先求解了 最大公因数,然后再进行表示。 使用c++ 实现,那么时间复杂度是$O(n^2)$

在 c++ 中使用 double long 处理精度的问题,但是可以使用gcb(最大公约数) 更加合理。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
class Solution {
public:
    // 首先这个是一个  $n^2$ 的算法,因为需要计算每一个点到其他的所有点的斜率(如果可以的话)
    int maxPoints(vector<vector<int>>& points) {
        int n =points.size();
        int res =0;
        // 以这个点为基准点 进行发射
        for(int i =0; i< n; i++)
        {
            unordered_map<double long, int> hash;
            int veticles =1, duplicates =0;
            
            for(int j =i +1; j<n; j++)
            {
                if(points[i][0] ==points[j][0])
                {
                    veticles +=1;
                    if(points[i][1] ==points[j][1]) duplicates +=1;
                }
            }
            for(int j =i +1; j<n; j++)
            {
                if(points[i][0] != points[j][0])
                {
                    double long slope = (double long)(points[i][1] -points[j][1])/(points[i][0] -points[j][0]);
                    if(hash[slope] ==0) hash[slope] =2;
                    else hash[slope ] ++;
                    
                    res =max(res, hash[slope] + duplicates);
                }
            }
            res =max(res, veticles);
        }
        return res;
    }
};

使用python 实现的

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
class Solution(object):
    # 在python 中 使用 gcb 处理精度问题
    def maxPoints(self, points):
        """
        :type points: List[List[int]]
        :rtype: int
        """
        res =0
        n =len(points)
        
        for i in range(n):
            veticles =1
            duplicates =0
            dic ={}
            for j in range(i+1, n):
                # 对应的是不能计算 slope 斜率的情况
                if(points[i][0] ==points[j][0]):
                    veticles +=1;
                    if(points[i][1] ==points[j][1]):
                        duplicates +=1
            
            for j in range(i +1, n):
                if points[i][0] != points[j][0]:
                    dx =points[i][0] -points[j][0]
                    dy =points[i][1] -points[j][1]
                    common =self.gcb(dx, dy)
                    if (dx//common, dy//common) in dic:
                        dic[(dx//common, dy //common)] +=1
                    else: 
                        dic[(dx//common, dy//common)] =2
                    res =max(res, dic[(dx//common, dy//common)] +duplicates)
            res =max(res, veticles)
        return res;
    def gcb(self, a, b):
        if(b ==0): return a
        return self.gcb(b, a%b)
    
        

279. Perfect Squares

时间复杂度 $O(n\sqrt(n))$,复杂度的计算是有个技巧,当 $i =n$ 时候,那么 $j$ 的遍历次数最多是 $\sqrt(n)$ 所以两者相乘就可以了。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
class Solution {
public:
    // 动态规划求解  f[i] 表示n 所需要的最少的数量
    // f[i] =min(f[i -j*j]) 其中  1<= j<= sqrt(n)
    // 最后的f[n] 表示最后的解
    int numSquares(int n) {
        // 按照道理求解最小值,可以初始化为 INT_MAX 这样的数字
        vector<int> f(n+1, n);
        f[0] =0; 
        for(int i =1; i<=n; i++)
        {
            for(int j =1; j*j <=i; j++)
               f[i] =min(f[i], f[i-j*j] +1);
        }
        return f[n];
    }
};

python 中使用这样的方式进行实现。关键是 sqrt(i) 是可以使用 i**0.5 进行实现的。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
class Solution(object):
    def numSquares(self, n):
        """
        :type n: int
        :rtype: int
        """
        nums =[n] *(n+1)
        nums[0] =0
        for i in range(1, n+1):
            for j in range(1, int(i**0.5)+1):
                nums[i] =min(nums[i], nums[i -j*j] +1)
        return nums[n]

python 有两种写法,一种是for 小暖使用 **0.5 表示 $sqrt(x) $ 的计算,一种是使用 while 循环方式。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
class Solution(object):
    # 能够想到的就是枚举, 从 1 到 sqrt(n) ,然后最后的时间复杂度是 nsqrt(n)
    def numSquares(self, n):
        """
        :type n: int
        :rtype: int
        """
        f=[n]*(n+1)
        f[0] =0
        for i in range(1, n+1):
            j =1
            while j*j <=i:
                f[i] =min(f[i], f[i -j*j] +1)
                j +=1
        return f[n]

69. Sqrt(x)

1

1

LeetCode 647. Palindromic Substrings

每次固定回文子串的中间位置,然后向左右开始扩展;每次固定后,分为奇数长度和偶数长度两种情况,然后暴力统计答案即可。 时间复杂度 共有 $O(n)$ 个中间位置,固定后,最坏情况下需要$ O(n)$ 的时间扩展回文串,故总时间复杂度为 $O(n^2)$。

使用 while 更加能够体现条件循环的精髓.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
class Solution {
public:
    int countSubstrings(string s) {
        int n =s.length();
        int res =0;
        // 这个是遍历中间结点的,
        for(int i =0; i<n; i++)
        {
            int j =0;
            while( i-j >=0 && i +j <n && s[i-j] ==s[i+j])
            {
                res +=1;
                j +=1;
            }
            j =1;
            while(i-j +1 >=0 && i+j <n && s[i-j +1] == s[i+j])
            {
                res +=1;
                j +=1;
            }
        }
        return res;
    }
};

使用python 实现

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
class Solution:
    def countSubstrings(self, s: str) -> int:
        n =len(s)
        res =0
        for i in range(n):
            j =0
            
            while i-j>=0 and i+j <n and s[i-j] ==s[i+j]:
                res +=1
                j +=1
            j =1
            while i-j+1 >=0 and i +j <n and s[i-j +1] ==s[i+j]:
                res +=1
                j +=1
        return res;

cpp 中小技巧,循环输入直到遇到回车。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
#include<stdio.h>
#include<iostream>
#include<string>
using namespace std;
int main()
{
    // 时间复杂度是 O(n^2)
    string str;
    while(getline(cin, str))
    {
        //if(s[0] =="\n") break;
        int ans =0;
        int n =str.length();
        for (int i = 0; i < n; i++) {
            for (int j = 0; i - j >= 0 && i + j < n && str[i - j] == str[i + j]; j++)
                ans++;
            for (int j = 1; i - j + 1 >= 0 && i + j < n && str[i - j + 1] == str[i + j]; j++)
                ans++;
        }
        cout << ans << endl;
        // 如果不是回车,那么就一直循环,如果是回车,那么就跳出
        if(getchar() =='\n') break;
    }
    return 0;
}

394. Decode String

这个题目使用 python 求解,因为python 中有比较多的字符串处理的函数 isdigit() 。整体上是 stack 的应用。貌似不太好分析时间复杂度。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
class Solution(object):
    def decodeString(self, s):
        """
        :type s: str
        :rtype: str
        """
        stk =[]
        for ch in s:
            if ch != ']':
                stk.append(ch)
            else:
                t =""
                while stk:
                    ch = stk.pop()
                    if ch != '[':
                        t = ch +t
                    else:
                        num = ''
                        while stk and stk[-1].isdigit():
                            num = stk.pop() + num
                        t =int(num) * t
                        stk.append(t)
                        break
        return "".join(stk)

String Compression

和上面一道题目相反的是,压缩字符串,关键是双指针的算法,不难。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
class Solution(object):
    def compress(self, chars):
        """
        :type chars: List[str]
        :rtype: int
        """
        n =len(chars)
        res =""
        i =0
        # 如果这里使用 range() 那么是无视下面对于i 的修改的,所以
        while i<n:
        #for i in range(n):
            j =i
            while j< n and chars[j] == chars[i] : j +=1 # 这个是常见的字符串计数的方式
            if j-i >1:        
                res += chars[i] +str(j -i)
            else:
                # 如果只是有一个的话,是不需要计数的
                res += chars[i]
            i =j
        chars[:] =list(res) # 这个是作弊的操作,因为本文要求的是 in-place 的操作,所以按照道理说是不应该申请新的内存的
        return len(chars)
        

48. Rotate Image

顺时针选择数组,可以分成两个步骤。

  1. 按照 y =x 轴对称旋转数组
  2. 按照 y的中点 的轴对称 旋转数组

时间复杂度是 $O(n^2)$

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
class Solution {
public:
    // 关键是思路的问题, rotate image ,首先是按照 y =x 进行对称; 然后是按照 竖坐标轴中间进行对称
    void rotate(vector<vector<int>>& matrix) {
        if(matrix.empty()) return;
        int n =matrix.size();
        for (int i =0; i<n; i++)
        {
            for(int j =i+1; j<n; j ++)
            {
                swap(matrix[i][j] ,matrix[j][i]);
            }
            // 第二个转换
            int m =matrix[0].size();
            for(int j=0, k =m -1; j<k ; j ++, k--)
                swap(matrix[i][j], matrix[i][k]);
        }      
    }
};

python 实现

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
class Solution:
    def rotate(self, matrix: List[List[int]]) -> None:
        """
        Do not return anything, modify matrix in-place instead.
        """
        n, m =len(matrix), len(matrix[0])
        for i in range(n):    
            # 这个在python中实现是比较nice的
            for j in range(i+1, n):
                matrix[i][j], matrix[j][i] =matrix[j][i], matrix[i][j]    
            j, k =0, m-1
            while(j <k):
                matrix[i][k], matrix[i][j] =matrix[i][j], matrix[i][k]
                j +=1
                k -=1

扩展, 如果是逆时针旋转数组,那么这个时候,可以先按照 y =-x 进行旋转,然后按照 y的中点旋转数组。

54. Spiral Matrix

这个比较简单,是四个for 循环遍历解决的问题,边界问题是需要注意的。还没有找到除此之外其他的方法。下面的代码是错误的,边界问题太难处理了。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
class Solution(object):
    def spiralOrder(self, matrix):
        """
        :type matrix: List[List[int]]
        :rtype: List[int]
        """
        
        if not matrix: return []
        
        top, down =0, len(matrix)-1
        left, right =0, len(matrix[0]) -1
        res =[]
        
        while left <= right and top <= down:
            res += matrix[top][:]
            
            for i in range(top+1, down):
                res.append(matrix[right][i])
            res += matrix[down][:][::-1]
            
            for i in range(down -1, top, -1):
                res.append(matrix[left][i])
        return res
    

 除了使用完整的切片 [:]操作,还可以使用一下的 [left: right] 这种切片形式。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
class Solution(object):
    def spiralOrder(self, matrix):
        """
        :type matrix: List[List[int]]
        :rtype: List[int]
        """
        if not matrix : return matrix
        top, down =0, len(matrix) -1
        left, right =0, len(matrix[0]) -1
        res =[]
        while top < down and left < right:
            
            res += matrix[top][left: right]
            
            for i in range(top, down):
                res.append( matrix[i][right])
            
            res += matrix[down][left: right +1][::-1]
            for i in range(down-1, top, -1):
                res.append( matrix[i][left])
            top +=1
            down -=1
            left +=1
            right -=1
            
        if top ==down:
            res += matrix[top][left: right+1]
        elif left ==right:
            for i in range(top, down +1):
                res.append(matrix[i][left])
        return res
    

上面使用的是更新完一次,然后更新

使用类似的版本的代码, 边界问题通过多个 break语句进行处理, exactly 的方式进行处理。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
class Solution {
public:
    // 边界问题如何处理
    vector<int> spiralOrder(vector<vector<int>>& matrix) {
        vector<int> res;
        int n = matrix.size();
        if(!n) return  res;
        int m =matrix[0].size();
        int l=0, r=m-1, u=0, d=n-1;
        while (true){
            if(l<=r)
                for(int i =l ; i<=r; i++) res.push_back(matrix[u][i]);
            else break;
            u +=1;
            if(u <= d)
                for(int i =u ; i<=d; i++) res.push_back(matrix[i][r]);
            else break;
            r -=1;
            if(l <=r)
                for(int i=r; i>=l; i--) res.push_back(matrix[d][i]);
            else break;
            d -=1;
            if(u<=d)
                for(int i=d; i>=u; i--) res.push_back(matrix[i][l]);
            else break;
            l +=1;
        }
        return res;
    }
};

[https://leetcode.com/problems/remove-duplicates-from-sorted-list-ii/](Remove Duplicates from Sorted List II)

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode(int x) : val(x), next(NULL) {}
 * };
 */
class Solution {
public:
    // 这个是常常考察的笔试题
    ListNode* deleteDuplicates(ListNode* head) {
        ListNode * dummy = new ListNode(-1);
        dummy ->next =head;
        ListNode * p =dummy;
        while(p ->next)
        {
            auto q =p->next;
            while(q && q->val ==p->next->val) q = q->next;
            // if 中表示的没有重复的元素, else中表示有重复的元素
            if(p->next ->next ==q) p =p->next;
            else p->next =q;
        }
        return dummy->next;
    }
};

python 实现。 注意在新建对象中 python 是直接 ListNode 但是 c++ 中需要new 关键字。然后指针的使用 c++ 中 -> 然后python 中使用 . 关键字.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
# Definition for singly-linked list.
# class ListNode(object):
#     def __init__(self, x):
#         self.val = x
#         self.next = None
class Solution(object):
    def deleteDuplicates(self, head):
        """
        :type head: ListNode
        :rtype: ListNode
        """
        # 有一些细节需要注意
        dummy = ListNode(-1)
        dummy.next =head
        p = dummy
        while( p.next):
            q =p.next
            while(q and p.next.val == q.val):
                q =q.next
            if(p.next.next ==q):
                p=p.next;
            else:
                p.next =q
        return dummy.next

Remove Duplicates from Sorted List

其中的 if 表示并没有舍弃当前的结点,每次都是前进一步。和上面那道题中,使用while 找到最后的结点不同。一个个进行操作,因为其中使用的 if 语句。上面的全部去掉使用的是 while 语句。

python 版本

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
# Definition for singly-linked list.
# class ListNode(object):
#     def __init__(self, x):
#         self.val = x
#         self.next = None
# 这道题目相对上一道题目就比较简单,
class Solution(object):
    def deleteDuplicates(self, head):
        """
        :type head: ListNode
        :rtype: ListNode
        """
        if not head: return head;
        p =head
        while p.next: 
            if(p.val == p.next.val):
                p.next =p.next.next
            else:
                p =p.next
        return head    

c++ 版本

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode(int x) : val(x), next(NULL) {}
 * };
 */
class Solution {
public:
    ListNode* deleteDuplicates(ListNode* head) {
        if(! head) return head;   
        ListNode* p =head;
        while(p->next)
        {
            // 这句话并没有舍弃当前的结点,每次都是移动一步,
            if(p->next->val == p->val) p->next= p->next->next;
            else p =p->next;
        }
        return head;
    }
};