文章目录
1642. 可以到达的最远建筑(优先队列)
最开始想到的方法是贪心,寻找合适的位置用梯子。但是这样会被hack的,没有想到使用优先队列的方法。但是想到了的话还是很自然的,维护最大的N个代价即可。
class Solution {
public int furthestBuilding(int[] heights, int bricks, int ladders) {
int n = heights.length, sum = 0;
Queue<Integer> queue = new PriorityQueue<>();
for(int i = 1; i < heights.length; i++) {
int diff = heights[i] - heights[i - 1];
if(diff > 0) {
queue.offer(diff);
if(queue.size() > ladders) {
sum += queue.poll();
}
if(sum > bricks)
return i - 1;
}
}
return n - 1;
}
}
1643. 第 K 条最小指令H(组合数计数)
是一道利用通过组合数问题比较好的练习题。也是记住组合数的模板,C[i][j]
表示
C
i
j
C_i^j
Cij.
class Solution {
int[][] C;
public String kthSmallestPath(int[] d, int k) {
int v = d[0];
int h = d[1];
C = new int[h+v+1][h+v+1];
make(h+v);
char[] ans = new char[h+v];
int sum = h+v;
for (int i = 0; i<sum;i++){
if (C[h+v-1][v]>=k){// 注意这里的关键是等于,此时也是写H,不清楚就自己写一下。
ans[i] = 'H';
h--;
}else{
ans[i] = 'V';
k -= C[h+v-1][v];
v--;
}
}
return String.valueOf(ans);
}
private void make(int n){
for (int i = 0; i<=n;i++){
for (int j = 0; j<=i;j++){
if (j == 0) C[i][j] = 1;
else C[i][j] = (C[i-1][j]+C[i-1][j-1]);
}
}
}
}
1638. 统计只差一个字符的子串数目(最长公共子串DP)
比较暴力的方法是
n
4
n^4
n4的,依次枚举长度,s串的起始位置,t串的起始位置和匹配位置。比较聪明的方法是类似经典DP问题,最长公共子串的。dp[i][j]
表示s
结尾i
,t
结尾j
的情况下,一个不同的字符的数目。类似的我们还需要维护一个完全相同的字串的个数。
// dp方法,类似与字符串匹配
class Solution {
public int countSubstrings(String s, String t) {
int slength = s.length();
int tlength = t.length();
int[][] dp = new int[slength][tlength];
int[][] dpEqual = new int[slength][tlength];// dp[i][j] 表示s结尾i,t结尾j的情况下,完全的数目
int sum = 0;
for(int i=0;i<slength;i++){
for(int j=0;j<tlength;j++){
if(i==0||j==0){// 初始
if(s.charAt(i)==t.charAt(j)){
dp[i][j]=0;
dpEqual[i][j]=1;
}else if(s.charAt(i)!=t.charAt(j)){
dp[i][j]=1;
dpEqual[i][j]=0;
}
}else{
if(s.charAt(i)==t.charAt(j)){
dp[i][j]=dp[i-1][j-1]; // 这里是转移,继续眼神
dpEqual[i][j]=dpEqual[i-1][j-1]+1;// 维护最长的相同
}else if(s.charAt(i)!=t.charAt(j)){
dp[i][j]=dpEqual[i-1][j-1]+1; //之前最长的,加上自己
dpEqual[i][j]=0;
}
}
sum=sum+dp[i][j];
}
}
return sum;
}
}
1639. 通过给定词典构造目标字符串的方案数H(Dp)
是一道典型的DP问题,但是这个DP的设计决定了复杂度。比较好的设计思路是dp[i][j
]第i位用前j个字符,转移方程dp[i][j] = dp[i-1][j-1]*maplen+dp[i][j-1]);
这样复杂度是
n
2
n^2
n2。也可以设计为dp[i][j]
表示target第i位用第j个字符,转移方程为dp[i][j] =dp[i-1][k]*maplen+dp[i][j]
,需要额外枚举k,表示第i-1位使用哪一个字符,这样的复杂度是
n
3
n^3
n3。
//dp[i][j]第i位用前j个
class Solution {
public int numWays(String[] words, String target) {
int n = words.length;
int m = words[0].length();
int[][] map = new int[m][26];
// 设计了查表,降低了复杂度
for (int i = 0; i<n;i++){
for (int j = 0; j<m;j++){
map[j][(int)(words[i].charAt(j)-'a')]++;
}
}
int Mod = 1000000007;
int len = target.length();
int[][] dp = new int[len][m];
dp[0][0] = map[0][target.charAt(0)-'a'];
for (int i = 1; i<m;i++){
dp[0][i] = (int)((1L*dp[0][i-1] + map[i][target.charAt(0)-'a'])%Mod);
}
for (int i = 1;i<len;i++){
for (int j = i;j<m;j++){ // 剪枝
int maplen = map[j][(int)target.charAt(i)-'a'];
dp[i][j] = (int)((1L*dp[i-1][j-1]*maplen+1L*dp[i][j-1])%Mod);
}
}
return dp[len-1][m-1];
}
}
=======================================================================================
// dp[i][j] 表示target第i位用第j个字符
class Solution {
public int numWays(String[] words, String target) {
int n = words.length;
int m = words[0].length();
int[][] map = new int[m][26];
for (int i = 0; i<n;i++){
for (int j = 0; j<m;j++){
map[j][(int)(words[i].charAt(j)-'a')]++;
}
}
int Mod = 1000000007;
int len = target.length();
int[][] dp = new int[len][m];
for (int i = 0;i<len;i++){
for (int j = i;j<=m-len+i;j++){ // 剪枝
int maplen = map[j][(int)target.charAt(i)-'a'];
if (maplen==0) continue;
if (i == 0){
dp[i][j] = maplen;
continue;
}
for (int k = i-1; k<j;k++){
dp[i][j] = (int)((1L*dp[i-1][k]*maplen+1L*dp[i][j])%Mod);
}
}
}
int ans =0;
for (int i = 0; i<m;i++){
ans = (int)((1L*ans+dp[len-1][i])%Mod);
}
return ans;
}
}