小美喜欢字母E,讨厌字母F。在小美生日时,小团送了小美一个仅包含字母E和F的字符串,小美想从中选出一个包含字母E数量与字母F数量之差最大的子串。
*子串:从字符串前面连续删去若干个字符,从后面连续删去若干个字符剩下的字符串(也可以一个都不删),例如abcab是fabcab的子串,而不是abcad的子串。我们将空串看作所有字符串的子串。
小美喜欢字母E,讨厌字母F。在小美生日时,小团送了小美一个仅包含字母E和F的字符串,小美想从中选出一个包含字母E数量与字母F数量之差最大的子串。
*子串:从字符串前面连续删去若干个字符,从后面连续删去若干个字符剩下的字符串(也可以一个都不删),例如abcab是fabcab的子串,而不是abcad的子串。我们将空串看作所有字符串的子串。
第一行一个正整数n表示字符串的长度。
第二行长度为n,且仅包含大写字母’E’,’F’的字符串(不含引号)
输出一个整数,表示最大的差值
5 EFEEF
2
选择子串EE,此时有2个E,0个F,有最大差值2-0=2
另外,选择子串EFEE也可以达到最大差值。
对于30%的数据,n<=300
对于60%的数据,n<=3000
对于100%的数据,n<=300000
//贪心,像PAT的最大子列和那题,我的算法启蒙,浙大数据结构的第一讲 #include<iostream> #include<string> using namespace std; int main() { int n; cin >> n; string s; cin >> s; int cnt[2] = {0}; int ans = 0; for (int i = 0; i < n; ++i) { cnt[s[i] - 'E']++; if (cnt[0] < cnt[1]) { cnt[0] = 0; cnt[1] = 0; } ans = max(ans, cnt[0] - cnt[1]); } cout << ans << endl; return 0; }
package main import "fmt" func EF() { var N int var str string fmt.Scan(&N,&str) cur,max:=0,0 for i:=0;i<N;i++{ if str[i]=='E' { cur++ if i==N-1&&cur>max{ max=cur } }else { if cur>max{ max=cur } cur-- if cur<0{ cur=0 } } } fmt.Println(max) } func main(){ EF() }
import java.io.BufferedReader; import java.io.InputStreamReader; import java.io.IOException; public class Main { public static void main(String[] args) throws IOException { BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); int n = Integer.parseInt(br.readLine().trim()); char[] str = br.readLine().trim().toCharArray(); int maxSum = 0, sum = 0; for(int i = 0; i < n; i++){ if(str[i] == 'E') sum ++; if(str[i] == 'F') sum --; maxSum = Math.max(maxSum, sum); sum = Math.max(sum, 0); } System.out.println(maxSum); } }
import java.util.Scanner; public class Main { public static void main(String[] args) { Scanner sc = new Scanner(System.in); int n = sc.nextInt(); String gift = sc.next(); // 将字符串转换为数字数组 int[] giftNum = new int[gift.length()]; for (int i = 0; i < gift.length(); i++) { // E转换为数字1,F转换为数字-1 giftNum[i] = gift.charAt(i) == 'E' ? 1 : -1; } method(giftNum, n); } // 转换为数字数组之后,这题就相当于求最大子序和了 public static void method(int[] gift, int n) { // 初始化dp数组 int[] dp = new int[n]; dp[0] = Math.max(gift[0], 0); int max = dp[0]; // 遍历,根据递推求最大子串和 for (int i = 1; i < dp.length; i++) { // 每次到达一个新的数字,考虑是加上当前数组总和大还是重头开始总和大 dp[i] = Math.max(dp[i - 1] + gift[i], gift[i]); // 不断更新最大值 max = Math.max(dp[i], max); } System.out.println(max); } }
import java.util.*; public class Main { public static void main(String[] args) { Scanner sc = new Scanner(System.in); int len = sc.nextInt(); String str = sc.next(); int[] dp = new int[len]; if (str.charAt(0)=='E') dp[0] = 1; else dp[0] = 0; for (int i = 1; i < len; i++) { if (str.charAt(i) == 'E') { dp[i] = dp[i-1] + 1; } else { dp[i] = Math.max(dp[i-1] - 1, 0); } } int max = Integer.MIN_VALUE; for (int i = 0; i < dp.length; i++) { max = Math.max(max, dp[i]); } System.out.println(max); } }
import java.util.Scanner; public class Main { public static void main(String[] args) { Scanner sc = new Scanner(System.in); int n = sc.nextInt(); char[] chs = sc.next().toCharArray(); //eNum表示E的数量减去F的数量,如果小于0,重新开始计数,也就是滑动窗口的左端点移至当前下标 int res = 1, eNum = 0; for (int i = 0; i < n; i++) { if (chs[i] == 'E') eNum++; else if (--eNum < 0) eNum = 0; res = Math.max(res, eNum); } System.out.println(res); } }如果是绝对值,多加一个变量,同样的思想:
import java.util.Scanner; public class Main { public static void main(String[] args) { Scanner sc = new Scanner(System.in); int n = sc.nextInt(); char[] chs = sc.next().toCharArray(); //eNum表示E的数量减去F的数量,如果小于0,重新开始计数,也就是滑动窗口的左端点移至当前下标 //fNum表示F的数量减去E的数量,如果小于0,重新开始计数 int res = 1, eNum = 0, fNUm = 0; for (int i = 0; i < n; i++) { if (chs[i] == 'E') { eNum++; if (--fNUm < 0) fNUm = 0; } else { fNUm++; if (--eNum < 0) eNum = 0; } res = Math.max(res, Math.max(eNum, fNUm)); } System.out.println(res); } }
import java.util.*; public class Main { public static void main(String[] args) { Scanner sc = new Scanner(System.in); int n = sc.nextInt(); String s = sc.next(); int[] dp = new int[n]; int flag = 0; if (s.charAt(0) == 'E') { dp[0] = 1; } else { dp[0] = -1; } int max = Integer.MIN_VALUE; for (int i = 1; i < n; i++) { if (s.charAt(i) == 'E') { flag = 1; } else { flag = -1; } dp[i] = Math.max(flag,dp[i - 1] + flag); if (dp[i] > max) { max = dp[i]; } } System.out.println(max); } }
using namespace std; #include<iostream> #include<string> #include<vector> int main(){ int n ; cin >> n; string str; cin >> str; vector<int> dp(n,0);//dp[i] :包括第i位置之前的“E和F”的差值 if(str[0] == 'E'){//初始化,判断第一个字母是什么 dp[0] = 1; } else{ dp[0] = -1; } int ans = 0; for(int i = 1; i < n; i++){ if(str[i] == 'E'){//如果当前位置的字母为E,肯定选取当前位置,在前一个位置的基础上+1; dp[i] = dp[i-1]+1; } else{//如果当前位置的字母是F,判断要不要当前位置,如果要,因为是F所以-1;如果不要重新计数,重置为0; dp[i] = max(dp[i-1]-1,0); } ans = max(ans,dp[i]);//选取一个最大值 } cout << ans <<'\n'; }
#include <bits/stdc++.h> using namespace std; int main() { int n; cin >> n; string str; cin >> str; vector<int> prefixCt(n + 1, 0); for (int i = 1; i <= n; ++i) { prefixCt[i] = (str[i] == 'E' ? 1 : -1) + prefixCt[i - 1]; } int minCt = 0; int result = 0; for (int i = 1; i <= n; ++i) { result = max(prefixCt[i] - minCt, result); minCt = min(prefixCt[i], minCt); } cout << result; }
看到“最大“的字样想到动态规划
先将EF字符串转换为1,-1的数组nums
然后明确dp,dp[i]代表以nums[i]结尾的子串的最大和
明确dp方程,dp[i]由前一项和nums[i]决定,如果前一项小于0,则去掉前面的项
// 动态规划 let n = ~~readline() let str = readline().split('') let dp = [] // 将EF替换为1,-1 let nums = str.map(item => item == 'E' ? 1 : -1) // 初始化dp数组 dp[0] = nums[0] // 初始化最大差值 let max = nums[0] for(let i = 1 ;i<n;i++){ dp[i] = Math.max(dp[i-1], 0) + nums[i] max = Math.max(dp[i] ,max) } // 依据题意,差值最小时为全F的情况,输出0 print(max >= 0 ? max : 0)
n = int(input()) s = input() res = 0 cur = 0 curMin = 0 for i in range(n): if s[i] == 'E': v = 1 else: v = -1 cur += v curMin = min(curMin, cur) res = max(res, cur - curMin) print(res)