一维数组的动态和
先从简单开始,没看题解完成简单的动态规划;
给你一个数组 nums 。数组「动态和」的计算公式为:runningSum[i] = sum(nums[0]…nums[i]) 。
请返回 nums 的动态和。
示例 1:
输入:nums = [1,2,3,4]
输出:[1,3,6,10]
解释:动态和计算过程为 [1, 1+2, 1+2+3, 1+2+3+4] 。
示例 2:
输入:nums = [1,1,1,1,1]
输出:[1,2,3,4,5]
解释:动态和计算过程为 [1, 1+1, 1+1+1, 1+1+1+1, 1+1+1+1+1] 。
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/running-sum-of-1d-array
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
这道题其实就是累加,想想累加的特性,前几个数的和再加上当前数,这道题将我们每一步算下来的值都保存。
public int[] runningSum(int[] nums) {
if(nums==null||nums.length==0){
return new int[0];
}
int len=nums.length;
int []dp=new int [len];
dp[0]=nums[0];
for(int i=1;i<len;i++){
dp[i]=nums[i]+dp[i-1];
}
return dp;
}
滑动窗口的最大值
这个题也是简单题,坚持不看题解;
给定一个数组 nums 和滑动窗口的大小 k,请找出所有滑动窗口里的最大值。
示例:
输入: nums = [1,3,-1,-3,5,3,6,7], 和 k = 3
输出: [3,3,5,5,6,7]
解释:
滑动窗口的位置 最大值
--------------- -----
[1 3 -1] -3 5 3 6 7 3
1 [3 -1 -3] 5 3 6 7 3
1 3 [-1 -3 5] 3 6 7 5
1 3 -1 [-3 5 3] 6 7 5
1 3 -1 -3 [5 3 6] 7 6
1 3 -1 -3 5 [3 6 7] 7
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/hua-dong-chuang-kou-de-zui-da-zhi-lcof
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
这道题其实就是动态规划加双指针;
public int[] maxSlidingWindow(int[] nums, int k) {
if(nums==null||nums.length==0){
return new int[0];
}
int len=nums.length;
int []res=new int[len-k+1];
for(int i=0;i<len-k+1;i++){
res[i]=nums[i];
for(int j=i+1;j<i+k;j++){
res[i]=Math.max(res[i],nums[j]);
}
}
return res;
}
回文子串
给定一个字符串,你的任务是计算这个字符串中有多少个回文子串。
具有不同开始位置或结束位置的子串,即使是由相同的字符组成,也会被视作不同的子串。
示例 1:
输入:"abc"
输出:3
解释:三个回文子串: "a", "b", "c"
示例 2:
输入:"aaa"
输出:6
解释:6个回文子串: "a", "a", "a", "aa", "aa", "aaa"
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/palindromic-substrings
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
这道题属于中等范畴
这个题最先想到的办法就是将所有子串都列举出来,然后判断是不是回文子串;说实话,列举所有子串的算法我好像也不会;
看了官方题解:
1、中心扩展:枚举每一个可能的回文中心,然后用两个指针分别向左右两边拓展,当两个指针指向的元素相同时就扩展,否则停;
public int countSubStrings(String s){
int n=s.length(),res=0;
for(int i=0;i<2*n-1;i++){
int l=i/2,r=i/2+i%2;
while (l>=0&&r<n&&s.charAt(l)==s.charAt(r)){
--l;
++r;
++res;
}
}
return res;
}
这个中心扩展法很妙;
官方解题的思路,i<2*n-1控制的很妙,规律也找的很妙,记录回文中心;
2、Manacher算法:Manacher算法是在线性时间内求解最长回文子串的算法。
Manacher算法依旧需要枚举s的每一个位置并先假设它是回文中心,但是它会利用已经计算出来的状态来更新f(i),而不是向【中心拓展】一样盲目地扩展,具体地说,假设我们已经计算好了【1,i-1】区间内所有点的f(最大半径),那么我们也就知道了【1,i-1】拓展出的回文达到最大半径时的回文右端点i+f(i)-1.
那么Manacher算法是如何通过已经计算出的状态来更新f(i)呢?
Manacher算法要求我们维护【当前最大的回文的右端点Rm】以及这个右端点所对应的回文中心im,我们需要顺序遍历s,假设当前遍历的下标为i。 我们知道在求解f(i)之前我们应当已经得到了从【i,i-1】所有的f,并且当前已经有了一个最大回文右端点rm以及它对应的回文中心im。
初始化f(i):如果i被包含在当前最大的回文子串内,假设j是i关于这个最大回文的回文中心im的对称位置(即j+1=2*im),我们可以得到f(i)至少等于min{f(i),rm-i+1}。保证这个回文串在当前最大回文串内。
中心扩展:做完初始化后,我们可以保证此时的s【i+f(i)-1】=s【i-f(i)+1】,要继续扩展这个区间,我们就要继续判断s【i+f(i)】和s【i-f(i)】是否相等,如果相等将f(i)自增;这样循环直到s【i+f(i)】不等于s【i-f(i)】。需要注意下标不能越界,这里使用的办法是在开头加一个$结尾加一个!,这样开头和结尾两个字符一定不相等,循环就可以在这里终止。
这样我们就可以得到s所有点为中心的最大回文半径,也能够得到s中所有可能的回文中心的最大回文半径,把它们累加便可。
public int countSubStrings1(String s){
int n=s.length();
StringBuffer t=new StringBuffer("$#");
for(int i=0;i<n;i++){
t.append(s.charAt(i));
t.append('#');
}
t.append('!');
int[] f=new int[t.length()];
int iMax=0,rMax=0,ans=0;
for(int i=1;i<t.length();i++){
f[i]=i<=rMax?Math.min(rMax-i+1,f[2*iMax-1]):1;
while (t.charAt(i+f[i])==t.charAt(i-f[i])){
++f[i];
}
//
if(i+f[i]-1>rMax){
iMax=i;
rMax=i+f[i]-1;
}
//
ans+=f[i]/2;
}
return ans;
}
这个算法我还没理解透,完了再看吧;