mirror of
https://github.com/Estom/notes.git
synced 2026-04-09 05:48:31 +08:00
fasdf
This commit is contained in:
@@ -26,10 +26,10 @@ n*2 | 等价于 左移一位 n << 1
|
||||
## 3 常见算法
|
||||
|
||||
### 快速幂
|
||||
* 使用二进制方法,将幂转换成二进制。二进制每个位的权重就是可以递推计算,与二分法效果相同。
|
||||
|
||||
```
|
||||
double myPow(double x, int n) {
|
||||
//使用二进制方法,将幂转换成二进制。二进制每个位的权重就是可以递推计算,与二分法效果相同。
|
||||
long long N=n;
|
||||
if(N<0){
|
||||
x=1/x;
|
||||
@@ -43,10 +43,23 @@ double myPow(double x, int n) {
|
||||
N= N>>1;
|
||||
}
|
||||
return result;
|
||||
|
||||
```
|
||||
* 使用二分法、递归的方式求解快速幂。
|
||||
```
|
||||
//数学类题目。使用快速幂
|
||||
long long multi(long long x,int n){
|
||||
if(n == 0) return 1;
|
||||
if(n == 1) return x;
|
||||
long long half = multi(x, n / 2);
|
||||
long long mod = multi(x, n % 2);
|
||||
return (half * half * mod)%1000000007;
|
||||
}
|
||||
```
|
||||
|
||||
### 快速乘法
|
||||
|
||||
* 二进制的列竖式思想。
|
||||
```
|
||||
int quickMulti(int A, int B) {
|
||||
int ans = 0;
|
||||
@@ -61,6 +74,8 @@ int quickMulti(int A, int B) {
|
||||
|
||||
### 快速加法
|
||||
|
||||
* 利用按位与和按位异或运算。求解加法。
|
||||
* 循环加余法。
|
||||
```
|
||||
int add(int a,int b){
|
||||
cout<<(unsigned int)-1<<endl;
|
||||
|
||||
@@ -54,34 +54,36 @@ $$
|
||||
3. 写出状态转移方程如果给定第k段状态变量xk的值,则该段的决策变量uk一经确定,第k+1段状态变量xk+1的值也就完全确定。
|
||||
4. 列出指标函数Vk,n 关系,并要满足递推性。
|
||||
|
||||
|
||||
### 动态规划最关键的部分
|
||||
* 确定规模的增长方向。一般在动态规划问题中。规模可变的并不只有一个。比如在正则表达式与字符串的匹配问题中。字符串的规模可以变化,正则表达式的规模也可以变化。
|
||||
|
||||
## 1 常见问题
|
||||
|
||||
### 最长公共子序列
|
||||
|
||||
|
||||
### 矩阵连乘问题
|
||||
### 1矩阵连乘问题
|
||||
|
||||
### 凸多边形最优三角剖分
|
||||
### 2凸多边形最优三角剖分
|
||||
|
||||
### 最长公共子序列
|
||||
### 3 最长公共子序列
|
||||
|
||||
### 图像压缩问题
|
||||
### 4 斐波那契数列
|
||||
|
||||
### 最大子段和问题
|
||||
### 5图像压缩问题
|
||||
|
||||
### 流水作业调度问题
|
||||
### 6最大子段和问题
|
||||
|
||||
### 投资问题
|
||||
### 7流水作业调度问题
|
||||
|
||||
### 01背包问题
|
||||
### 8投资问题
|
||||
|
||||
### 0n背包问题
|
||||
### 9 01背包问题
|
||||
|
||||
### 最优二叉搜索树问题
|
||||
### 10 0n背包问题
|
||||
|
||||
### 序列匹配问题
|
||||
### 11最优二叉搜索树问题
|
||||
|
||||
### 12 序列匹配问题
|
||||
|
||||
----
|
||||
|
||||
|
||||
0
算法/A类:基本算法/5.10 完全背包问题.md
Normal file
0
算法/A类:基本算法/5.10 完全背包问题.md
Normal file
0
算法/A类:基本算法/5.11 最优二叉搜索树问题.md
Normal file
0
算法/A类:基本算法/5.11 最优二叉搜索树问题.md
Normal file
0
算法/A类:基本算法/5.5 图像压缩问题.md
Normal file
0
算法/A类:基本算法/5.5 图像压缩问题.md
Normal file
0
算法/A类:基本算法/5.6 最大子段和问题.md
Normal file
0
算法/A类:基本算法/5.6 最大子段和问题.md
Normal file
0
算法/A类:基本算法/5.7 作业调度问题.md
Normal file
0
算法/A类:基本算法/5.7 作业调度问题.md
Normal file
0
算法/A类:基本算法/5.8 投资问题.md
Normal file
0
算法/A类:基本算法/5.8 投资问题.md
Normal file
0
算法/A类:基本算法/5.9 01背包问题.md
Normal file
0
算法/A类:基本算法/5.9 01背包问题.md
Normal file
248
算法/B类:数据结构算法/4.1 反转链表.md
Normal file
248
算法/B类:数据结构算法/4.1 反转链表.md
Normal file
@@ -0,0 +1,248 @@
|
||||
# 反转链表
|
||||
|
||||
|
||||
## 1 从尾到头打印链表内容
|
||||
|
||||
### 问题描述
|
||||
|
||||
输入一个链表的头节点,从尾到头反过来返回每个节点的值(用数组返回)。
|
||||
|
||||
### 问题分类
|
||||
* 线性数据结构
|
||||
* 枚举法
|
||||
* 排序问题
|
||||
|
||||
### 问题分析
|
||||
|
||||
## 1.1 从尾到头打印链表内容——辅助栈
|
||||
|
||||
### 策略选择
|
||||
|
||||
* 数据结构:链表、栈、数组
|
||||
* 算法思想:枚举法
|
||||
* 可以利用栈的先进后出特性。
|
||||
### 算法设计
|
||||
|
||||
|
||||
1. 入栈: 遍历链表,将各节点值 push 入栈。(Python 使用 append() 方法,Java借助 LinkedList 的addLast()方法)。
|
||||
2. 出栈: 将各节点值 pop 出栈,存储于数组并返回。(Python 直接返回 stack 的倒序列表,Java 新建一个数组,通过 popLast() 方法将各元素存入数组,实现倒序输出)。
|
||||
### 算法分析
|
||||
* 时间复杂度O(n)
|
||||
* 空间复杂度O(n)
|
||||
|
||||
### 算法实现
|
||||
|
||||
```
|
||||
vector<int> reversePrint1(ListNode* head) {
|
||||
stack<int> s;
|
||||
vector<int> v;
|
||||
ListNode * temp=head;
|
||||
while(temp){
|
||||
s.push(temp->val);
|
||||
temp=temp->next;
|
||||
}
|
||||
while(!s.empty()){
|
||||
v.push_back(s.top());
|
||||
s.pop();
|
||||
}
|
||||
return v;
|
||||
}
|
||||
```
|
||||
|
||||
## 1.2 从尾到头打印链表内容——递归法
|
||||
|
||||
### 策略选择
|
||||
|
||||
* 数据结构:链表
|
||||
* 算法思想:枚举法
|
||||
* 利用递归后续遍历的特性(子节点在父节点之前除了)
|
||||
|
||||
### 算法设计
|
||||
|
||||
1. 递推阶段: 每次传入 head.next ,以 head == None(即走过链表尾部节点)为递归终止条件,此时返回空列表 [] 。
|
||||
2. 回溯阶段: 利用 Python 语言特性,递归回溯时每次返回 当前 list + 当前节点值 [head.val] ,即可实现节点的倒序输出。
|
||||
|
||||
### 算法分析
|
||||
|
||||
* 时间复杂度O(n)
|
||||
* 空闲复杂度O(n)
|
||||
|
||||
### 算法实现
|
||||
|
||||
```C++
|
||||
vector<int> reversePrint(ListNode* head) {
|
||||
vector<int> vec;
|
||||
rp(head,vec);
|
||||
return vec;
|
||||
}
|
||||
void rp(ListNode* head,vector<int>&vec) {
|
||||
if(head==nullptr)return ;
|
||||
rp(head->next,vec);
|
||||
vec.push_back(head->val);
|
||||
return ;
|
||||
}
|
||||
```
|
||||
|
||||
## 2 反转链表
|
||||
|
||||
### 问题描述
|
||||
|
||||
* 给你单链表的头指针 head 和两个整数 left 和 right ,其中 left <= right 。请你反转从位置 left 到位置 right 的链表节点,返回 反转后的链表 。
|
||||
[链接](https://leetcode-cn.com/problems/reverse-linked-list-ii)
|
||||
|
||||
## 2.1 反转链表——普通循环反转
|
||||
### 策略选择
|
||||
|
||||
* 循环
|
||||
|
||||
### 算法设计
|
||||
|
||||
* 在每次循环的时候。到达一个节点。
|
||||
* 记录本节点的下一个节点
|
||||
* 记录本节点的上一个几点
|
||||
* 反转本次节点指向上一个节点
|
||||
* 复制本层节点= 下一个节点。开始下一次循环。
|
||||
|
||||
|
||||
### 算法分析
|
||||
|
||||
* 时间复杂度O(n)
|
||||
* 空间复杂度O(n)
|
||||
|
||||
### 算法实现
|
||||
```C++
|
||||
ListNode* reverseBetween(ListNode* head, int left, int right) {
|
||||
int i=1;
|
||||
ListNode* node=head;
|
||||
ListNode* last_node=nullptr;
|
||||
ListNode* before_left=nullptr;
|
||||
if(left==1){
|
||||
before_left=new ListNode();
|
||||
before_left->next=head;
|
||||
}
|
||||
ListNode* after_node;
|
||||
while(true){
|
||||
after_node=node->next;
|
||||
if(i==left-1){
|
||||
before_left=node;
|
||||
}
|
||||
if(i>left && i<=right){
|
||||
node->next=last_node;
|
||||
}
|
||||
if(i==right){
|
||||
before_left->next->next = after_node;
|
||||
before_left->next=node;
|
||||
break;
|
||||
}
|
||||
last_node=node;
|
||||
node=after_node;
|
||||
i++;
|
||||
}
|
||||
if(left==1)return before_left->next;
|
||||
return head;
|
||||
}
|
||||
```
|
||||
|
||||
## 2.2 反转链表——头插法反转
|
||||
|
||||
### 策略选择
|
||||
* 循环
|
||||
* 插入反转
|
||||
|
||||
### 算法设计
|
||||
|
||||
* 每次循环的时候。到达本节点。将本节点的下一个节点插入到左节点前的下一个节点。从left开始
|
||||
* temp保留该节点的下一个几点。
|
||||
* 当前节点指向下一个节点的下一个节点(删除下一个节点)
|
||||
* temp指向左前节点的下一个节点
|
||||
* 左前节点指向该节点(插入下一个几点)
|
||||
|
||||
### 算法分析
|
||||
|
||||
* 时间复杂度O(n)
|
||||
* 空间复杂度O(1)
|
||||
|
||||
### 算法实现
|
||||
|
||||
```
|
||||
// 头插法
|
||||
ListNode* reverseBetween2(ListNode* head, int left, int right) {
|
||||
ListNode* before_head = new ListNode();
|
||||
before_head->next=head;
|
||||
ListNode* before_left;
|
||||
ListNode* node=before_head;
|
||||
ListNode* temp;
|
||||
for(int i=0;i<=right;i++){
|
||||
if(i==left-1)before_left=node;
|
||||
if(i>=left && i<right){
|
||||
temp = node->next;
|
||||
node->next = temp->next;
|
||||
temp->next=before_left->next;
|
||||
before_left->next=temp;
|
||||
}
|
||||
else{
|
||||
node=node->next;
|
||||
}
|
||||
}
|
||||
return before_head->next;
|
||||
}
|
||||
```
|
||||
|
||||
## 链表反转——递归法
|
||||
|
||||
### 策略选择
|
||||
|
||||
* 递归法
|
||||
* 等价于使用栈保存了节点的路径。不会在反转后回不到过去的节点
|
||||
|
||||
### 算法设计
|
||||
|
||||
* 递归
|
||||
* 当到右节点,使用全局变量记录右节点和右节点的下一个节点。
|
||||
* 当在左右之间时,直接使自身的下一个节点指向自己。
|
||||
* 当到达左节点时。与有节点和有节点的下一个反转。
|
||||
|
||||
* 递归的参数
|
||||
* 递归的返回值
|
||||
* 递归的执行
|
||||
* 递归的终止条件
|
||||
* 递归前的处理和递归后的处理。
|
||||
|
||||
|
||||
### 算法分析
|
||||
|
||||
* 时间复杂度O(n)
|
||||
* 空间复杂度O(n)
|
||||
|
||||
### 算法实现
|
||||
|
||||
```C++
|
||||
// 递归法,把它当做只有一侧的树。后续遍历。取得时候将节点路径放到栈里。回来的时候直接反转也能返回最初的节点。
|
||||
ListNode* reverseBetween(ListNode* head, int left, int right) {
|
||||
ListNode* before_head = new ListNode();
|
||||
before_head->next=head;
|
||||
dfs(before_head,left,right,0);
|
||||
return before_head->next;
|
||||
}
|
||||
ListNode* right;
|
||||
ListNode* right_next;
|
||||
void dfs(ListNode* head,int left,int right,int i){
|
||||
if(i>right)return ;
|
||||
// cout<<i<<endl;
|
||||
dfs(head->next,left,right,i+1);
|
||||
if(i>=left && i<right){
|
||||
head->next->next=head;//下一个节点指向自己
|
||||
}
|
||||
if(i==right){
|
||||
this->right=head;
|
||||
this->right_next=head->next;
|
||||
// cout<<head->val<<"feji"<<endl;
|
||||
|
||||
}
|
||||
if(i==left-1){
|
||||
head->next->next=this->right_next;
|
||||
head->next=this->right;
|
||||
}
|
||||
return ;
|
||||
}
|
||||
```
|
||||
47
算法/B类:数据结构算法/4.2 链表与双指针.md
Normal file
47
算法/B类:数据结构算法/4.2 链表与双指针.md
Normal file
@@ -0,0 +1,47 @@
|
||||
# 链表与双指针
|
||||
|
||||
|
||||
## 1 链表中的倒数第k个节点
|
||||
|
||||
### 问题描述
|
||||
|
||||
* 输入一个链表,输出该链表中倒数第k个节点。为了符合大多数人的习惯,本题从1开始计数,即链表的尾节点是倒数第1个节点。例如,一个链表有 6 个节点,从头节点开始,它们的值依次是 1、2、3、4、5、6。这个链表的倒数第 3 个节点是值为 4 的节点。
|
||||
|
||||
### 问题分析
|
||||
|
||||
* 典型的双指针问题
|
||||
|
||||
### 问题分类
|
||||
|
||||
* 数组与链表
|
||||
* 双指针问题
|
||||
|
||||
### 策略选择
|
||||
|
||||
* 蛮力法
|
||||
|
||||
### 算法流程
|
||||
|
||||
* 两个一样快的指针。相距k个距离。
|
||||
* 一个到达终点,另一个则为倒数第k个节点
|
||||
|
||||
### 算法分析
|
||||
* 时间复杂度O(n)
|
||||
* 空间复杂度O(1)
|
||||
|
||||
### 算法实现
|
||||
|
||||
```C++
|
||||
ListNode* getKthFromEnd(ListNode* head, int k) {
|
||||
ListNode* first=head;
|
||||
ListNode* second=head;
|
||||
while(--k){
|
||||
second=second->next;
|
||||
}
|
||||
while(second->next){
|
||||
second = second->next;
|
||||
first = first->next;
|
||||
}
|
||||
return first;
|
||||
}
|
||||
```
|
||||
@@ -1,4 +1,3 @@
|
||||
|
||||
## 1 重复数字问题
|
||||
|
||||
### 问题描述
|
||||
|
||||
@@ -1,85 +0,0 @@
|
||||
# 反转链表
|
||||
|
||||
|
||||
## 1 从尾到头打印链表内容
|
||||
|
||||
### 问题描述
|
||||
|
||||
输入一个链表的头节点,从尾到头反过来返回每个节点的值(用数组返回)。
|
||||
|
||||
### 问题分类
|
||||
* 线性数据结构
|
||||
* 枚举法
|
||||
* 排序问题
|
||||
|
||||
### 问题分析
|
||||
|
||||
## 1.1 从尾到头打印链表内容——辅助栈
|
||||
|
||||
### 策略选择
|
||||
|
||||
* 数据结构:链表、栈、数组
|
||||
* 算法思想:枚举法
|
||||
* 可以利用栈的先进后出特性。
|
||||
### 算法设计
|
||||
|
||||
|
||||
1. 入栈: 遍历链表,将各节点值 push 入栈。(Python 使用 append() 方法,Java借助 LinkedList 的addLast()方法)。
|
||||
2. 出栈: 将各节点值 pop 出栈,存储于数组并返回。(Python 直接返回 stack 的倒序列表,Java 新建一个数组,通过 popLast() 方法将各元素存入数组,实现倒序输出)。
|
||||
### 算法分析
|
||||
* 时间复杂度O(n)
|
||||
* 空间复杂度O(n)
|
||||
|
||||
### 算法实现
|
||||
|
||||
```
|
||||
vector<int> reversePrint1(ListNode* head) {
|
||||
stack<int> s;
|
||||
vector<int> v;
|
||||
ListNode * temp=head;
|
||||
while(temp){
|
||||
s.push(temp->val);
|
||||
temp=temp->next;
|
||||
}
|
||||
while(!s.empty()){
|
||||
v.push_back(s.top());
|
||||
s.pop();
|
||||
}
|
||||
return v;
|
||||
}
|
||||
```
|
||||
|
||||
## 1.2 从尾到头打印链表内容——递归法
|
||||
|
||||
### 策略选择
|
||||
|
||||
* 数据结构:链表
|
||||
* 算法思想:枚举法
|
||||
* 利用递归后续遍历的特性(子节点在父节点之前除了)
|
||||
|
||||
### 算法设计
|
||||
|
||||
1. 递推阶段: 每次传入 head.next ,以 head == None(即走过链表尾部节点)为递归终止条件,此时返回空列表 [] 。
|
||||
2. 回溯阶段: 利用 Python 语言特性,递归回溯时每次返回 当前 list + 当前节点值 [head.val] ,即可实现节点的倒序输出。
|
||||
|
||||
### 算法分析
|
||||
|
||||
* 时间复杂度O(n)
|
||||
* 空闲复杂度O(n)
|
||||
|
||||
### 算法实现
|
||||
|
||||
```
|
||||
vector<int> reversePrint(ListNode* head) {
|
||||
vector<int> vec;
|
||||
rp(head,vec);
|
||||
return vec;
|
||||
}
|
||||
void rp(ListNode* head,vector<int>&vec) {
|
||||
if(head==nullptr)return ;
|
||||
rp(head->next,vec);
|
||||
vec.push_back(head->val);
|
||||
return ;
|
||||
}
|
||||
```
|
||||
|
||||
139
算法/C类:问题类型算法/4.1 正则表达式匹配问题.md
Normal file
139
算法/C类:问题类型算法/4.1 正则表达式匹配问题.md
Normal file
@@ -0,0 +1,139 @@
|
||||
# 正则表达式匹配问题
|
||||
|
||||
## 1 正则表达式匹配
|
||||
|
||||
### 问题描述
|
||||
|
||||
* 请实现一个函数用来匹配包含'\.'和'\*'的正则表达式。模式中的字符'\.'表示任意一个字符,而'\*'表示它前面的字符可以出现任意次(含0次)。在本题中,匹配是指字符串的所有字符匹配整个模式。例如,字符串"aaa"与模式"a.a"和"ab*ac*a"匹配,但与"aa.a"和"ab*a"均不匹配。
|
||||
|
||||
### 问题分析
|
||||
|
||||
* 可以使用动态规划。将字符串的规模增长的方向,作为动态变化的方向。状态变量表示当前的正则表达式,能够与之匹配。当规模发生变化时,保证字符串-1和正则表达式-1能够匹配。其实在这里感觉规模增长的方向,确实是有两个。但是状态变量的设置比较精巧。
|
||||
|
||||
### 问题分类
|
||||
|
||||
* 字符串匹配问题
|
||||
* 动态规划
|
||||
* 递归
|
||||
|
||||
|
||||
## 1.1 正则表达式匹配——动态规划
|
||||
|
||||
### 选择策略
|
||||
* 动态规划
|
||||
|
||||
### 算法设计
|
||||
|
||||
* 设s的长度为n ,pp 的长度为 m ;将 s 的第 i 个字符记为 s_i,p 的第 j个字符记为 p_j ,将 s 的前 i 个字符组成的子字符串记为s[:i] , 同理将 p 的前 j 个字符组成的子字符串记为 p[:j]p[:j] 。
|
||||
|
||||
* 因此,本题可转化为求s[:n] 是否能和p[:m] 匹配。
|
||||
|
||||
* 总体思路是从 s[:1]和 p[:1]是否能匹配开始判断,每轮添加一个字符并判断是否能匹配,直至添加完整个字符串s 和p 。展开来看,假设 s[:i]与 p[:j]可以匹配,那么下一状态有两种:
|
||||
1. 添加一个字符 $s_{i+1}$后是否能匹配?
|
||||
2. 添加字符 $p_{j+1}$后是否能匹配?
|
||||
|
||||
* 本题的状态共有 m \times nm×n 种,应定义状态矩阵 dpdp ,dp[i][j]代表 s[:i]与 p[:j]是否可以匹配。做好状态定义,接下来就是根据 「普通字符」 , 「.」 , 「*」三种字符的功能定义,分析出动态规划的转移方程。
|
||||
|
||||
1. **状态定义**: 设动态规划矩阵 dp , dp[i][j] 代表字符串 s 的前 i 个字符和 p 的前 j 个字符能否匹配。
|
||||
2. **转移方程**: 需要注意,由于 dp[0][0] 代表的是空字符的状态, 因此 dp[i][j] 对应的添加字符是 s[i - 1] 和 p[j - 1] 。
|
||||
* 当 p[j - 1] = '*' 时, dp[i][j] 在当以下任一情况为 truetrue 时等于 truetrue :
|
||||
1. dp[i][j - 2]: 即将字符组合 p[j - 2] * 看作出现 0 次时,能否匹配.
|
||||
2. dp[i - 1][j] 且 s[i - 1] = p[j - 2]: 即让字符 p[j - 2] 多出现 1 次时,能否匹配;
|
||||
3. dp[i - 1][j] 且 p[j - 2] = '.': 即让字符 '.' 多出现 1 次时,能否匹配;
|
||||
* 当 p[j - 1] != '*' 时, dp[i][j] 在当以下任一情况为 truetrue 时等于 truetrue :
|
||||
1. dp[i - 1][j - 1] 且 s[i - 1] = p[j - 1]: 即让字符 p[j - 1] 多出现一次时,能否匹配;
|
||||
2. dp[i - 1][j - 1] 且 p[j - 1] = '.': 即将字符 . 看作字符 s[i - 1] 时,能否匹配;
|
||||
* **初始化**: 需要先初始化 dp 矩阵首行,以避免状态转移时索引越界。
|
||||
* **返回值** dp 矩阵右下角字符,代表字符串 s 和 p 能否匹配。
|
||||
|
||||

|
||||
|
||||
### 算法分析
|
||||
|
||||
* 时间复杂度O(M*N)
|
||||
* 空间复杂度O(M*N)
|
||||
|
||||
### 算法实现
|
||||
|
||||
```C++
|
||||
class Solution {
|
||||
public:
|
||||
bool isMatch(string s, string p) {
|
||||
int m = s.size() + 1, n = p.size() + 1;
|
||||
vector<vector<bool>> dp(m, vector<bool>(n, false));
|
||||
dp[0][0] = true;
|
||||
for(int j = 2; j < n; j += 2)
|
||||
dp[0][j] = dp[0][j - 2] && p[j - 1] == '*';
|
||||
for(int i = 1; i < m; i++) {
|
||||
for(int j = 1; j < n; j++) {
|
||||
dp[i][j] = p[j - 1] == '*' ?
|
||||
dp[i][j - 2] || dp[i - 1][j] && (s[i - 1] == p[j - 2] || p[j - 2] == '.'):
|
||||
dp[i - 1][j - 1] && (p[j - 1] == '.' || s[i - 1] == p[j - 1]);
|
||||
}
|
||||
}
|
||||
return dp[m - 1][n - 1];
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
## 1.2 正则表达式匹配——递归
|
||||
|
||||
|
||||
### 选择策略
|
||||
* 递归
|
||||
|
||||
### 算法设计
|
||||
|
||||
* 递归的参数:字符串string、模式pattern、int i、int j分别表示当前字符串匹配到的位置,和模式行进到的位置。
|
||||
* 递归的返回值:返回值是当前分支是否成功。如果最后一个检测成果了,则返回true。如果最后没有检测成功,则返回false;
|
||||
* 递归的执行:
|
||||
* 如果下一个是*号。进行多分支递归讨论。
|
||||
1. 匹配0次,i=i,j=j+2.进行下一轮递归。
|
||||
2. 匹配1次,判断这一次是否成功,如果成功i=i+1,j=j
|
||||
* 如果下一个不是*号。进行单分支递归。
|
||||
1. 匹配正常字符或者.。匹配成功。i=i+1,j=j+1
|
||||
2. 匹配不成功。return false;
|
||||
* 递归的终止条件
|
||||
* i和j匹配都完成。return true;
|
||||
* 其他情况return false
|
||||
* 递归前和递归后的处理
|
||||
|
||||
### 算法分析
|
||||
|
||||
* 时间复杂度O(M+N)
|
||||
* 空间复杂度O(M)
|
||||
|
||||
### 算法实现
|
||||
|
||||
```C++
|
||||
bool isMatch(string s, string p) {
|
||||
return isMatchR(s,p,0,0);
|
||||
}
|
||||
// 递归和循环混搭,果然很恶心。直接使用递归好了。
|
||||
// 递归的条件判断。正则表达式。
|
||||
// 算法设计的关键是,选择一个好的算法技术(递归或循环,两个是冗余的。)
|
||||
// 然后设计好的算法路程。关键在于分类讨论的方式。如何合并类别。
|
||||
bool isMatchR(string &s,string &p,int i,int j){
|
||||
//递归终止的条件
|
||||
if(i>=s.size() && j>=p.size()){
|
||||
return true;
|
||||
}
|
||||
// cout<<i<<j<<endl;
|
||||
// *判断。
|
||||
if(j+1<p.size() && p[j+1]=='*'){
|
||||
// 匹配零次 || 匹配一次
|
||||
return isMatchR(s,p,i,j+2) || ((i<s.size()) && (p[j]==s[i]||p[j]=='.')&&isMatchR(s,p,i+1,j));
|
||||
|
||||
}
|
||||
// 如果下一个不是*
|
||||
if(i<s.size() && j<p.size() && p[j]==s[i]){
|
||||
return isMatchR(s,p,i+1,j+1);
|
||||
}
|
||||
if(i<s.size() && j<p.size() && p[j]=='.'){
|
||||
return isMatchR(s,p,i+1,j+1);
|
||||
}
|
||||
// 不匹配
|
||||
return false;
|
||||
}
|
||||
```
|
||||
|
||||
147
算法/C类:问题类型算法/4.2 表示数值的字符串.md
Normal file
147
算法/C类:问题类型算法/4.2 表示数值的字符串.md
Normal file
@@ -0,0 +1,147 @@
|
||||
# 表示数值的字符串
|
||||
|
||||
## 1 表示数值的字符串
|
||||
|
||||
### 问题描述
|
||||
|
||||
* 请实现一个函数用来判断字符串是否表示数值(包括整数和小数)。例如,字符串"+100"、"5e2"、"-123"、"3.1416"、"-1E-16"、"0123"都表示数值,但"12e"、"1a3.14"、"1.2.3"、"+-5"及"12e+5.4"都不是。
|
||||
* [链接](https://leetcode-cn.com/problems/biao-shi-shu-zhi-de-zi-fu-chuan-lcof)
|
||||
|
||||
|
||||
### 问题分析
|
||||
|
||||
* 这题一看,明显就是有限状态自动机。词法分析过程中用来判断关键字、数值的。属于暴力破解。使用优先状态自动机。确定分类讨论的情况。找到符合规则的所有的路。
|
||||
* 另外提供了另外一种暴力破解的思路。通过讨论违反规则的情况。找到违反规则的所有的路。
|
||||
|
||||
* 提供了两种截然不同的分类讨论的思路。在某些情况下,第二种思路反而会简单很多。
|
||||
|
||||
* 在 C++ 文档 中,描述了一个合法的数值字符串应当具有的格式。具体而言,它包含以下部分:
|
||||
* 符号位,即 +、− 两种符号
|
||||
* 整数部分,即由若干字符 0-9组成的字符串
|
||||
* 小数点
|
||||
* 小数部分,其构成与整数部分相同
|
||||
* 指数部分,其中包含开头的字符 e(大写小写均可)、可选的符号位,和整数部分
|
||||
|
||||
### 问题分类
|
||||
|
||||
* 枚举法
|
||||
* 正向分类讨论和反向分类讨论
|
||||
|
||||
## 1.1 表示数值的字符串——有限状态自动机DFA
|
||||
|
||||
### 选择策略
|
||||
|
||||
* 有限状态自动机
|
||||
* 正向分类讨论
|
||||
|
||||
### 算法设计
|
||||
|
||||
* 字符类型:空格 「 」、数字「 0—9 」 、正负号 「 +− 」 、小数点 「 .」 、幂符号 「 eE 」
|
||||
* 状态定义:按照字符串从左到右的顺序,定义以下 9 种状态。
|
||||
1. 开始的空格
|
||||
2. 幂符号前的正负号
|
||||
3. 小数点前的数字
|
||||
4. 小数点、小数点后的数字
|
||||
5. 当小数点前为空格时,小数点、小数点后的数字
|
||||
6. 幂符号
|
||||
7. 幂符号后的正负号
|
||||
8. 幂符号后的数字
|
||||
9. 结尾的空格
|
||||
|
||||
* 状态转移图
|
||||

|
||||
|
||||
|
||||
### 算法分析
|
||||
|
||||
* 时间复杂度O(n)
|
||||
* 空间复杂度O(n)
|
||||
|
||||
### 算法实现
|
||||
|
||||
```C++
|
||||
// 方法一:有限状态自动机DFA,时间复杂度 O(N)
|
||||
typedef pair<char,int> charint;
|
||||
typedef unordered_map<char,int> unmap;
|
||||
bool isNumber(string s) {
|
||||
vector<unmap> states = {
|
||||
unmap{charint(' ',0),charint('s',1),charint('d',2),charint('.',4)},
|
||||
unmap{charint('d',2),charint('.',4)},
|
||||
unmap{charint('d',2),charint('.',3),charint('e',5),charint(' ',8)},
|
||||
unmap{charint('d',3),charint('e',5),charint(' ',8)},
|
||||
unmap{charint('d',3)},
|
||||
unmap{charint('s',6),charint('d',7)},
|
||||
unmap{charint('d',7)},
|
||||
unmap{charint('d',7),charint(' ',8)},
|
||||
unmap{charint(' ',8)}
|
||||
};
|
||||
int p = 0;
|
||||
char t;
|
||||
for(char c:s){
|
||||
if(c >= '0' && c <= '9')
|
||||
t = 'd';
|
||||
else if(c == '+' || c == '-')
|
||||
t = 's';
|
||||
else if(c == 'e' || c == 'E')
|
||||
t = 'e';
|
||||
else if(c == '.' || c == ' ')
|
||||
t = c;
|
||||
else
|
||||
t = '?';
|
||||
if(!states[p].count(t))
|
||||
return false;
|
||||
p = (int) states[p][t];
|
||||
}
|
||||
return p == 2 || p == 3 || p == 7 || p == 8;
|
||||
}
|
||||
```
|
||||
## 1.2 表示数值的字符串——反向分类讨论
|
||||
|
||||
### 算法设计
|
||||
|
||||
* 讨论所有可能出现的反例
|
||||
|
||||
* e/E 分割为指数和底数
|
||||
* 底数:
|
||||
* 只能有一个+-号位于第一位
|
||||
* 只能有一个小数点
|
||||
* 指数
|
||||
* 只能有一个+-号位于第一位
|
||||
* 不能有小数点
|
||||
|
||||
### 算法分析
|
||||
|
||||
* 时间复杂度O(n)
|
||||
* 空间复杂度O()
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
81
算法/C类:问题类型算法/8.1 和积最大.md
Normal file
81
算法/C类:问题类型算法/8.1 和积最大.md
Normal file
@@ -0,0 +1,81 @@
|
||||
# 和积最大
|
||||
|
||||
|
||||
## 1 剪绳子
|
||||
|
||||
### 问题描述
|
||||
* 给你一根长度为 n 的绳子,请把绳子剪成整数长度的 m 段(m、n都是整数,n>1并且m>1),每段绳子的长度记为 k[0],k[1]...k[m - 1] 。请问 k[0]*k[1]*...*k[m - 1] 可能的最大乘积是多少?例如,当绳子的长度是8时,我们把它剪成长度分别为2、3、3的三段,此时得到的最大乘积是18。
|
||||
|
||||
* 答案需要取模 1e9+7(1000000007),如计算初始结果为:1000000008,请返回 1。
|
||||
|
||||
[链接](https://leetcode-cn.com/problems/jian-sheng-zi-ii-lcof)
|
||||
|
||||
### 问题分析
|
||||
|
||||
|
||||
### 问题分类
|
||||
|
||||
* 贪心思想
|
||||
* 数值问题
|
||||
* 无数据结构
|
||||
|
||||
### 策略选择
|
||||
|
||||
* n尽可能多的包含因子3
|
||||
|
||||
### 算法设计
|
||||
1. 如果 n == 2,返回1,如果 n == 3,返回2,两个可以合并成n小于4的时候返回n - 1
|
||||
2. 如果 n == 4,返回4
|
||||
3. 如果 n > 4,分成尽可能多的长度为3的小段,每次循环长度n减去3,乘积res乘以3;最后返回时乘以小于等于4的最后一小段;每次乘法操作后记得取余就行
|
||||
4. 以上2和3可以合并
|
||||
### 算法分析
|
||||
* 时间复杂度O(logn)
|
||||
* 空间复杂度O(1)
|
||||
|
||||
### 算法实现
|
||||
```C++
|
||||
int cuttingRope1(int n) {
|
||||
//果然是数学问题
|
||||
// 这东西由3和2的组成。3越多值越大。也就是存在三种情况。
|
||||
// n能被3整除。则全为3
|
||||
// n被3整除余数为1,则减掉两个2后,全为3
|
||||
// n被3整除余2,减掉1个2后,全为3
|
||||
if(n==2){
|
||||
return 1;
|
||||
}
|
||||
if(n==3){
|
||||
return 2;
|
||||
}
|
||||
|
||||
if(n%3==0){
|
||||
return multi(3,n/3);
|
||||
}
|
||||
else if(n%3==1){
|
||||
return (4*multi(3,(n-4)/3))%1000000007;
|
||||
}
|
||||
else if(n%3==2){
|
||||
return (2*multi(3,(n-2)/3))%1000000007;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
//数学类题目。使用快速幂
|
||||
long long multi(long long x,int n){
|
||||
if(n == 0) return 1;
|
||||
if(n == 1) return x;
|
||||
long long half = multi(x, n / 2);
|
||||
long long mod = multi(x, n % 2);
|
||||
return (half * half * mod)%1000000007;
|
||||
}
|
||||
|
||||
int cuttingRope(int n) {
|
||||
if(n < 4){
|
||||
return n - 1;
|
||||
}
|
||||
long res = 1;
|
||||
while(n > 4){
|
||||
res = res * 3 % 1000000007;
|
||||
n -= 3;
|
||||
}
|
||||
return (int) (res * n % 1000000007);
|
||||
}
|
||||
```
|
||||
BIN
算法/C类:问题类型算法/image/2021-03-18-20-39-26.png
Normal file
BIN
算法/C类:问题类型算法/image/2021-03-18-20-39-26.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 77 KiB |
BIN
算法/C类:问题类型算法/image/2021-03-18-21-06-06.png
Normal file
BIN
算法/C类:问题类型算法/image/2021-03-18-21-06-06.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 92 KiB |
Reference in New Issue
Block a user