De Codechef :
Une chaîne est considérée équilibrée si et seulement si tous les caractères y figurent un nombre de fois égal.
Vous obtenez une chaîne
S
; cette chaîne ne peut contenir que des lettres anglaises majuscules. Vous pouvez effectuer l'opération suivante autant de fois (y compris zéro): Choisissez une lettre dansS
et remplacez-la par une autre lettre anglaise majuscule. Notez que même si la lettre remplacée se produit plusieurs fois dansS
, seule l'occurrence choisie de cette lettre est remplacée.Trouvez le nombre minimum d'opérations nécessaires pour convertir la chaîne donnée en une chaîne équilibrée.
Exemple:
Pour la saisie:
ABCB
Ici, nous pouvons remplacer
C
parA
, POUR OBTENIR:ABAB
, où chaque caractère de la chaîne se produit 2 fois.Donc, le nombre d'opération (s) minimum =
1
.
Comment rendre la chaîne bonne?
Puis-je lui appliquer une programmation dynamique?
Je ne pense pas que vous ayez vraiment besoin d'une programmation dynamique ici.
Une approche dans [~ # ~] o [~ # ~] (longueur ( [~ # ~] s [~ # ~] )) heure:
ABCB
, ce serait A->1 B->2 C->1 D->0 E->0 ... Z->0
, que nous pouvons représenter comme le tableau [1, 2, 1, 0, 0, ..., 0]
. ABCB
et ABBC
, en ce que chacun peut être équilibré en remplaçant leur C
par un A
.[0, 0, ..., 0, 1, 1, 2]
. ABCB
et ABDB
, en ce sens que chacun peut être équilibré en remplaçant une de leurs lettres singleton par l'autre.Le code suivant implémente la solution, en Java, avec test unitaire.
L'algorithme est presque identique à la réponse de @ ruakh, sinon identique.
BalanceString.Java
import Java.util.Arrays;
import Java.util.Collections;
import Java.util.HashMap;
import Java.util.Map;
/**
* Assume string only contains A-Z, the 26 uppercase letter,
* <p>given a string, you can replace a char with another char from the 26 letter,
* <p>figure out the minimum replacement required to make the string balance,
* <p>which means each char in the string occurs the same time,
*
* @author eric
* @date 2/2/19 8:54 PM
*/
public class BalanceString {
private final char minChar;
private final char maxChar;
private final int distinctChars; // total distinct char count,
public static final BalanceString EN_UPPER_INSTANCE = new BalanceString('A', 'Z');
public BalanceString(char minChar, char maxChar) {
this.minChar = minChar;
this.maxChar = maxChar;
this.distinctChars = maxChar - minChar + 1;
if (distinctChars <= 0)
throw new IllegalArgumentException("invalid range of chars: [" + minChar + ", " + maxChar + "]");
}
/**
* Check minimal moves needed to make string balanced.
*
* @param str
* @return count of moves
*/
public int balanceCount(String str) {
// System.out.printf("string to balance:\t%s\n", str);
int len = str.length(); // get length,
if (len <= 2) return 0; // corner cases,
Map<Character, Integer> coMap = figureOccurs(str); // figure occurrences,
Integer[] occurs = sortOccursReversely(coMap); // reversely order occurrences,
int m = coMap.size(); // distinct char count,
int maxN = (len < distinctChars ? len : distinctChars); // get max possible distinct char count, included,
int smallestMoves = Integer.MAX_VALUE; // smallest moves, among all possible n,
// check each possible n, and get its moves,
for (int n = 1; n <= maxN; n++) {
if (len % n == 0) {
int moves = figureMoves(len, coMap, occurs, m, n);
if (moves < smallestMoves) smallestMoves = moves;
}
}
return smallestMoves;
}
/**
* Figure occurs for each char.
*
* @param str
* @return
*/
protected Map<Character, Integer> figureOccurs(String str) {
Map<Character, Integer> coMap = new HashMap<>();
for (char c : str.toCharArray()) {
if (c < minChar || c > maxChar)
throw new IllegalArgumentException(c + " is not within range 'A-Z'");
if (!coMap.containsKey(c)) coMap.put(c, 1);
else coMap.put(c, coMap.get(c) + 1);
}
return coMap;
}
/**
* Get reverse sorted occurrences.
*
* @param coMap
* @return
*/
protected Integer[] sortOccursReversely(Map<Character, Integer> coMap) {
Integer[] occurs = new Integer[coMap.size()];
coMap.values().toArray(occurs);
Arrays.sort(occurs, Collections.reverseOrder());
return occurs;
}
/**
* Figure moves needed to balance.
*
* @param len length of string,
* @param coMap
* @param m original distinct elements count,
* @param n new distinct elements count,
* @return count of moves,
*/
protected int figureMoves(int len, Map<Character, Integer> coMap, Integer[] occurs, int m, int n) {
int avgOccur = len / n; // average occurrence,
int moves = 0;
if (n == m) { // distinct chars don't change,
for (Integer occur : occurs) {
if (occur <= avgOccur) break;
moves += (occur - avgOccur);
}
} else if (n < m) { // distinct chars decrease,
for (int i = 0; i < n; i++) moves += Math.abs(occurs[i] - avgOccur); // for elements kept,
for (int i = n; i < m; i++) moves += occurs[i]; // for elements to replace,
moves >>= 1;
} else { // distinct chars increase,
for (int i = 0; i < occurs.length; i++) moves += Math.abs(occurs[i] - avgOccur); // for existing elements,
moves += ((n - m) * avgOccur); // for new elements,
moves >>= 1;
}
return moves;
}
public char getMinChar() {
return minChar;
}
public char getMaxChar() {
return maxChar;
}
public int getDistinctChars() {
return distinctChars;
}
}
BalanceStringTest.Java
(Test unitaire, via TestNG
)
import org.testng.Assert;
import org.testng.annotations.Test;
/**
* BalanceString test.
*
* @author eric
* @date 2/2/19 9:36 PM
*/
public class BalanceStringTest {
private BalanceString bs = BalanceString.EN_UPPER_INSTANCE;
@Test
public void test() {
// n < m case,
Assert.assertEquals(bs.balanceCount("AAAABBBC"), 1); // e.g 1A -> B,
Assert.assertEquals(bs.balanceCount("AAAAABBC"), 2); // e.g 1A -> B, 1C -> B,
Assert.assertEquals(bs.balanceCount("AAAAAABC"), 2); // e.g 1B -> A, 1C -> A,
Assert.assertEquals(bs.balanceCount("AAAAAAAB"), 1); // e.g 1B -> A,
// n > m case,
Assert.assertEquals(bs.balanceCount("AAAABBBBCCCCDDDDEEEEAAAA"), 4); // add 1 new char, e.g change 4 A to 4 F,
Assert.assertEquals(bs.balanceCount(genIncSeq(10)), 15); // A-J, 10 distinct chars, 55 in length; solved by add 1 new char, need 15 steps,
// n == m case,
Assert.assertEquals(bs.balanceCount(genIncSeq(3)), 1); // A-C, 3 distinct chars, 6 in length; symmetric, solved with same distinct chars, need 1 steps,
Assert.assertEquals(bs.balanceCount(genIncSeq(11)), 15); // A-K, 11 distinct chars, 66 in length; symmetric, solved with same distinct chars, need 15 steps,
// n < m, or n > m case,
Assert.assertEquals(bs.balanceCount("ABAC"), 1); // e.g 1A -> B, or 1A -> D,
}
// corner case,
@Test
public void testCorner() {
// m <= 2,
Assert.assertEquals(bs.balanceCount(""), 0);
Assert.assertEquals(bs.balanceCount("A"), 0);
Assert.assertEquals(bs.balanceCount("AB"), 0);
Assert.assertEquals(bs.balanceCount("AA"), 0);
/*------ m == n == distinctChars ------*/
String mndBalanced = genMndBalancedSeq(); // each possible char occurs exactly once, already balanced,
Assert.assertEquals(mndBalanced.length(), bs.getDistinctChars());
Assert.assertEquals(bs.balanceCount(mndBalanced), 0); // no need change,
char lastChar = mndBalanced.charAt(mndBalanced.length() - 1);
String mndOneDup = mndBalanced.replace(lastChar, (char) (lastChar - 1)); // (distinctChars -2) chars occur exactly once, one occurs twice, one is missing, thus it's one step away to balance,
Assert.assertEquals(mndOneDup.length(), bs.getDistinctChars());
Assert.assertEquals(bs.balanceCount(mndOneDup), 1); // just replace the duplicate char with missing char,
}
// invalid input,
@Test(expectedExceptions = IllegalArgumentException.class)
public void testInvalidInput() {
Assert.assertEquals(bs.balanceCount("ABAc"), 1);
}
// invalid char range, for constructor,
@Test(expectedExceptions = IllegalArgumentException.class)
public void testInvalidRange() {
new BalanceString('z', 'a');
}
/**
* Generate a string, with first char occur once, second twice, third three times, and so on.
* <p>e.g A, ABB, ABBCCC, ABBCCCDDDD,
*
* @param m distinct char count,
* @return
*/
private String genIncSeq(int m) {
StringBuilder sb = new StringBuilder();
for (int i = 0; i < m; i++) {
for (int j = 0; j <= i; j++) sb.append((char) (bs.getMinChar() + i));
}
return sb.toString();
}
/**
* Generate a string that contains each possible char exactly once.
* <p>For [A-Z], it could be: "ABCDEFGHIJKLMNOPQRSTUVWXYZ".
*
* @return
*/
private String genMndBalancedSeq() {
StringBuilder sb = new StringBuilder();
char minChar = bs.getMinChar();
int distinctChars = bs.getDistinctChars();
for (int i = 0; i < distinctChars; i++) {
sb.append((char) (minChar + i));
}
return sb.toString();
}
}
Tous les cas de test réussiraient.
O(len)
+ O(m * lg(m))
+ O(m * factorCount)
O(len)
, il y a plusieurs boucles séquentielles.O(m*lg(m))
, qui est au plus O(distinctChars * lg(distinctChars))
, donc constant, et ne trie qu'une seule fois.O(m)
.minChar
, maxChar
].O(len)
O(len)
.O(m)
.O(m)
.Où:
len
est une longueur de chaîne.m
est un nombre de caractères distinct dans la chaîne d'originedistinctChars
est un nombre de caractères distinct, par exemple 26.maxN
nombre de caractères distincts maximum possible, inclus,factorCount
nombre divisible dans la plage [1, n]
, par len
,minChar
min char, par exemple AmaxChar
max char, par exemple ZEt:
len
> = m
m
<= distinctChars
if __name__ == "__main__":
for j in range(int(input())):
S = str(input())
N = len(S)
A = [0]*27
for c in S:
A[ord(c) - 65] = A[ord(c) - 65] + 1
A = sorted(A,reverse=True)
minSwap = N + 1
for i in range(1,27):
if N%i == 0:
temp = N//i
tempSwap = 0
for f in range(i):
if temp > A[f]:
tempSwap = tempSwap + temp - A[f]
if tempSwap <= minSwap:
minSwap = tempSwap
if minSwap == N+1:
minSwap = 0
print(minSwap)
#include <iostream>
#include <string>
#include <vector>
int countOps(std::vector<int> &map, int requiredFreq){
int countOps = 0, greaterFreq = 0, lesserFreq = 0;
for (auto a : map){
if (a > 0 && a < requiredFreq){
lesserFreq = lesserFreq + abs(requiredFreq - a);
}
else if (a > 0 && a > requiredFreq){
greaterFreq = greaterFreq + abs(requiredFreq - a);
}
}
countOps = greaterFreq > lesserFreq ? (lesserFreq + (greaterFreq - lesserFreq)) : lesserFreq;
return countOps;
}
int balanceString(std::string &s, long n){
std::vector<int> map(26, 0);
int distinctChar = 0;
int requiredFreq = -1;
int count = INT_MAX;
// Creating map with frequency and counting distinct chars
for (char x : s){
if (map[x - 'a'] == 0) distinctChar++;
map[x - 'a']++;
}
std::sort(std::begin(map), std::end(map), std::greater<int>{});
// If size is multiple of distinctChar
if (n % distinctChar == 0){
requiredFreq = int(n / distinctChar);
count = countOps(map, requiredFreq);
}
else{
for (int i = 1; i < distinctChar; i++){
if (n % i == 0){
requiredFreq = int(n / i);
std::vector<int> temp(map.begin(), map.begin() + i);
int x = countOps(temp, requiredFreq);
count = std::min(count, x);
}
}
}
return count;
}
int main(){
std::string s = "aaaaccddefghiii";
long n = s.size();
if(n <= 1) return 0;
int count = balanceString(s, n);
std::cout << count << std::endl;
return 0;
}
Pour résoudre ce problème, je pense qu'il est également utile de trouver partout une présence supplémentaire (somme du nombre d'éléments qui se présentent plus d'une fois) de différents éléments dans la chaîne
par exemple: dans
aabbc
, le nombre d'éléments que nous devons supprimer pour rendre la présence de chaque élément un est égal à 2 (this is called good string
)
`x=input()
char=26
total=0
lis=[0]*char
#print(lis)
for i in range(len(x)):
lis[ord(x[i])-ord('a')]+=1
#print(lis)
for i in range(26):
if(lis[i]>1):
total=total+(lis[i]-1)
print(total)
"