问题
字符串及其反转中是否存在同一子字符串
给你一个字符串s,请你判断字符串s是否存在一个长度为2的子字符串,在其反转后的字符串中也出现。
如果存在这样的子字符串,返回true;如果不存在,返回false。
解题思路
将字符串倒序排之后,每次截取两个字符判断是否存在题意中的字符串。
class Solution {
public boolean isSubstringPresent(String s) {
boolean res=false;
int n=s.length();
if (n<2){
return res;
}
String re=new StringBuffer(s).reverse().toString();
for(int i=0;i<n-1;i++){
if (re.contains(s.substring(i,i+2))){
return true;
}
}
return res;
}
}
统计以给定字符开头和结尾的子字符串总数
给你一个字符串s和一个字符c。返回在字符串s中并且以c字符开头和结尾的非空子字符串的总数。
解题思路
按题意推出只有1个字符时,出现次数是1;
只有两个字符时,出现次数是2;
只有三个字符时,出现次数是6;
有n次个字符时,出现次数是res[n-1]+k;
class Solution {
public long countSubstrings(String s, char c) {
long res=0;
int n=s.length();
int count=0;
for(int i=0;i<n;i++){
if (s.charAt(i)==c){
count++;
}
}
if (count==0){
return res;
}
if (count==1){
return 1;
}
if (count==2){
return 3;
}
int right=3;
res=3;
for(int i=3;i<=count;i++){
res=res+right;
right++;
}
return res;
}
}
成为 K 特殊字符串需要删除的最少字符数
给你一个字符串word和一个整数k。
如果|freq(word[i])-freq(word[j])|<=k对于字符串中所有下标i和j都成立,则认为word是k特殊字符串。
此处,freq(x)表示字符x在word中的出现频率,而|y|表示y的绝对值。
返回使word成为k特殊字符串需要删除的字符的最小数量。
解题思路
统计word中每个字母的出现次数,记到一个数组cnt中。
枚举i作为出现次数最小的字母,为了保留尽量多的字母,字母i肯定不需要删除。此外,出现次数最多的字母,其出现次数不能超过cnt[i]+k。
class Solution {
public int minimumDeletions(String word, int k) {
int[] cnt = new int[26];
for (char c : word.toCharArray()) {
cnt[c - 'a']++;
}
Arrays.sort(cnt);
int maxSave = 0;
for (int i = 0; i < 26; i++) {
int sum = 0;
for (int j = i; j < 26; j++) {
sum += Math.min(cnt[j], cnt[i] + k); // 至多保留 cnt[i]+k 个
}
maxSave = Math.max(maxSave, sum);
}
return word.length() - maxSave;
}
}
拾起 K 个 1 需要的最少行动次数
给你一个下标从0开始的二进制数组nums,其长度为n;另给你一个正整数k以及一个非负整数maxChanges。
灵茶山艾府在玩一个游戏,游戏的目标是让灵茶山艾府使用最少数量的行动次数从nums中拾起k个1。游戏开始时,灵茶山艾府可以选择数组[0,n-1]范围内的任何索引index站立。如果nums[index]==1,灵茶山艾府就会拾起一个1,并且nums[index]变成0(这不算作一次行动)。之后,灵茶山艾府可以执行任意数量的行动(包括零次),在每次行动中灵茶山艾府必须恰好执行以下动作之一:
选择任意一个下标j!=index且满足nums[j]==0,然后将nums[j]设置为1。这个动作最多可以执行maxChanges次。
选择任意两个相邻的下标x和y(|x-y|==1)且满足nums[x]==1,nums[y]0,然后交换它们的值(将nums[y]=1和nums[x]=0)。如果yindex,在这次行动后灵茶山艾府拾起一个1,并且nums[y]变成0。
返回灵茶山艾府拾起恰好k个1所需的最少行动次数。
解题思路
先用dylanIndex和其相邻的1,还有maxChanges来计算能不能取到k个1。如果可以那么其中的最小操作数量就是答案。如果取不了k个人,用滑动窗口计算取k-maxChanges要用到的最小操作(move)数量,加上2*maxChanges之后的最小操作数量就为答案。
class Solution {
public long minimumMoves(int[] nums, int k, int maxChanges) {
long res = Long.MAX_VALUE;
int n = nums.length;
long[] ps = new long[n];
int[] oct = new int[n];
long p = 0;
int pc = 0;
for (int i = 0; i < n; i++) {
if (nums[i] == 1) {
p += i;
pc++;
}
oct[i] = pc;
ps[i] = p;
}
for (int i = 0; i < n; i++) {
int t = k, op = 0;
if (nums[i] == 1)
t--;
if (t == 0) {
res = Math.min(res, op);
continue;
}
if (i != 0 && nums[i - 1] == 1) {
op++;
t--;
}
if (t == 0) {
res = Math.min(res, op);
continue;
}
if (i != n - 1 && nums[i + 1] == 1) {
op++;
t--;
}
if (t == 0) {
res = Math.min(res, op);
continue;
}
if (maxChanges >= t)
res = Math.min(res, op + t * 2);
}
int lt = 0, ct = 0;
int rk = k - maxChanges;
if (res == Long.MAX_VALUE)
for (int i = 0; i < n; i++) {
if (nums[i] == 1) {
ct++;
if (ct >= rk) {
while (nums[lt] == 0 || ct > rk) {
if (nums[lt] == 1)
ct--;
lt++;
}
int l1 = lt, r1 = i;
int m;
while (l1 < r1) {
m = l1 + r1 >> 1;
if (skCount(oct, lt, m) < skCount(oct, m, i))
l1 = m + 1;
else
r1 = m;
}
m = l1;
long t1 = skCount(ps, m + 1,i ) - (long) skCount(oct, m + 1,i ) * m;
long t2 = (long) (skCount(oct, lt,m )) * m - skCount(ps, lt,m );
res = Math.min(res, t1 + t2 + 2 * maxChanges);
}
}
}
return res;
}
public int skCount(int[] oct, int f, int t) {
return oct[t] - (f == 0 ? 0 : oct[f - 1]);
}
public long skCount(long[] oct, int f, int t) {
return oct[t] - (f == 0 ? 0 : oct[f - 1]);
}
}