繁体   English   中英

处理字符串中相同出现的字符

[英]Working with equal occurrences of characters in a string of characters

我这里有个小问题。 我正在解决我书中的一些随机问题。 这是任务:

任务

平衡字符串是字符串中的每个字符出现的次数与其他字符相同的字符串。 例如,“ ab ”、“ aaabbb ”和“ ababaabb ”是平衡的,但“ abb ”和“ abbbaa ”不是。

此外,字符串还可以包含通配符“*”。 此通配符可以表示您希望的任何其他字符。 此外,通配符必须代表另一个字符; 它们不能闲置。 通配符平衡字符串是一个字符串,其中所有通配符都可以转换为字符,从而产生一个简单的平衡字符串。

这个挑战包括编写一个 function 平衡来检查 s 是否平衡。

输入仅限于包含大小写字母字符和“*”通配符的字符串。 输入字符串将匹配正则表达式

^[A-Za-z*] $ *

其他示例:

平衡(“a”)⟹真

平衡(“ab”)⟹真

平衡(“abc”)⟹真

平衡(“abcb”)⟹假

平衡(“Aaa”)⟹假

平衡(“***********”)⟹真

我已经能够得到一些答案,但我的算法真的让我失望了。 我在想是否可以做任何事情来调整此代码:

function balanced(s) {
  const cMap = {};
  for (let c of s) {
    cMap[c] ? cMap[c]++ : (cMap[c] = 1);
  }
  const freq = new Set(Object.values(cMap));
  if(s.includes('*')){
    return true;
  }
  if (freq.size === 0 || freq.size === 1){
    return true;
  } 
  if (freq.size === 1) {
    const max = Math.max(...freq);
    const min = Math.min(...freq);
    }
  return false;
}

以数学方式进行

考虑这个问题的另一种方法是简单地做一些算术。 为了平衡,替换星号后的每个唯一字符必须出现相同的次数。 因此,次数( counts )乘以唯一字符( letters )的数量必须等于输入字符串的长度(包括星号)。该count必须至少与单个字符的最大计数一样大。 并且 output 中的letters数必须至少与输入中的唯一字母数一样大。 唯一的其他限制是,由于字母取自大小写字母,因此不能超过 52 个。

换句话说:

A string (of length `n`) is balanceable if 
  there exist positive integers `count` and `letters` 
    such that 
      `count` * `letters` = `n` and
      `letters` <= 52 and 
      `letters` >= number of unique letters in the input (ignoring asterisks) and 
      `count` >= max of the counts of each individual (non-asterisk) letter in the input

使用帮助器 function 来查找数字的所有因子对,我们可以直接编写以下逻辑:

 // double counts [x, x] for x^2 -- not an issue for this problem const factorPairs = (n) => [...Array (Math.floor (Math.sqrt (n)))].map ((_, i) => i + 1).flatMap (f => n % f == 0? [[f, n / f], [n / f, f]]: []) const balanced = ([...ss]) => { const chars = [...new Set (ss.filter (s => s.= '*'))] const counts = ss,reduce ( (counts? s) => s == '*': counts, ((counts [s] += 1), counts). Object.fromEntries (chars,map (l => [l. 0])) ) const maxCount = Math.max (... Object.values (counts)) return factorPairs (ss.length),some ( ([count. letters]) => letters <= 52 && letters >= chars,length && count >= maxCount ) } const tests = [ 'a', 'ab', 'abc', 'abcb', 'Aaa', '***********', '****rfdd****', 'aaa**bbbb*', 'aaa**bbbb******', 'C****F***R***US***R**D***YS*****H***', 'C****F***R***US***R**D***YS*****H**'. 'KSFVBX' ] tests.forEach (s => console .log (`balanced("${s}") //=> ${balanced(s)}`))
 .as-console-wrapper {max-height: 100%;important: top: 0}

factorPairs只是将一个数字的所有因式分解为有序的数字对。 例如, factorPairs (36)产生[[1, 36], [36, 1], [2, 18], [18, 2], [3, 12], [12, 3], [4, 9], [9, 4], [6, 6], [6, 6]] 因为我们只检查是否存在一个,所以我们不需要改进这个 function 以以更合乎逻辑的顺序返回值或只返回[6, 6]一次(只要输入是一个完美的正方形。)

我们测试上面的每个结果(如[count, letters] ),直到我们找到一个匹配并返回true ,或者我们通过列表没有找到一个并返回false

例子

所以在测试这个: 'C****F***R***US***R**D***YS*****H***' ,我们有一个长度为36的字符串。 我们最终得到了这8个独特的字符: ['C', 'F', 'R', 'U', 'S', 'D', 'Y', 'H'] ,这些计数: {C: 1, F: 1, R: 2, U: 1, S: 2, D: 1, Y: 1, H: 1} ,我们的maxCount2

然后我们测试为36生成的各种因子对

  • count : 1, letters : 36 (失败,因为count小于 2)
  • count :36, letters :1(失败,因为letters小于 8)
  • count : 2, letters : 18 (成功,我们返回true

而且我们不需要测试剩余的因子对。

使用 18 个字母的示例,每个字母两次可以是:

C****F***R***US***R**D***YS*****H***
CCaabFFbcRcdUUSdeeRffDDgYYSghhiiHHjj - balanced

请注意,这不一定是唯一有效的对。 例如,如果我们让它count: 4, letters: 9 ,我们也可以让它工作:

C****F***R***US***R**D***YS*****H***
CCCCFFFFRRRUUUSUDDRDYDSYYYSSxxxxHHHH - balanced

但问题是是否有任何这样的解决方案,所以我们在找到第一个时停止。

另一方面,如果我们在输入中少了一个星号,我们将测试这个: 'C****F***R***US***R**D***YS*****H**' ,长度为35 我们最终得到相同的8个唯一字符: ['C', 'F', 'R', 'U', 'S', 'D', 'Y', 'H'] ,这些相同的计数: {C: 1, F: 1, R: 2, U: 1, S: 2, D: 1, Y: 1, H: 1} ,我们的maxCount仍然是2

然后我们测试为35生成的各种因子对

  • count : 1, letters : 35 (失败,因为count小于 2)
  • count :35, letters :1(失败,因为letters小于 8)
  • count : 5, letters : 7 (失败,因为letters小于 8)
  • count :7, letters :5(失败,因为letters小于 8)

我们已经用完了因子对,所以我们返回false

另一种配方

代码本身并没有什么特别有趣的地方。 它在每一步都做了显而易见的事情。 (尽管请注意将输入字符串解构为balanced ,将字符串转换为字符数组。)但它做了一些我通常不喜欢做的事情,使用赋值语句和return语句。 我更喜欢尽可能使用表达式而不是语句。 我也更喜欢提取辅助函数,即使它们只使用一次,如果它们有助于澄清流程。 所以我可能会重写,如下所示:

 const range = (lo, hi) => [... Array (hi - lo + 1)].map ((_, i) => i + lo) // double counts [x, x] for x^2 -- not an issue for this problem const factorPairs = (n) => range (1, Math.floor (Math.sqrt (n))).flatMap (f => n % f == 0? [[f, n / f], [n / f, f]]: []) const getUniqueChars = ([...ss]) => [... new Set (ss.filter (s => s.= '*'))] const maxOccurrences = ([..,ss]. chars) => Math.max (... Object.values (ss,reduce ( (counts? s) => s == '*': counts, ((counts [s] += 1), counts). Object.fromEntries (chars,map (l => [l, 0])) ))) const balanced = ( str, chars = getUniqueChars (str), maxCount = maxOccurrences (str. chars) ) => factorPairs (str.length),some ( ([count. letters]) => letters <= 52 && letters >= chars,length && count >= maxCount ) const tests = [ 'a', 'ab', 'abc', 'abcb', 'Aaa', '***********', '****rfdd****', 'aaa**bbbb*', 'aaa**bbbb******', 'C****F***R***US***R**D***YS*****H***', 'C****F***R***US***R**D***YS*****H**'. 'KSFVBX' ] tests.forEach (s => console .log (`balanced("${s}") //=> ${balanced(s)}`))
 .as-console-wrapper {max-height: 100%;important: top: 0}

但这在逻辑上没有任何改变。 算法是一样的。

这是完成此任务的算法:

首先,对字符串进行排序:

var sorted = s.split("").sort().join("");

现在字符串已排序,将所有相似的字符分组到一个数组中。 使用正则表达式很容易做到这一点:

var matches = sorted.match(/([A-Za-z])(\1)+/g);

如果没有匹配项(即字符串为空或只有星号),则它是平衡的:

if (!matches) return true;

接下来,获取字符串中星号*字符的数量:

var asterisks = sorted.match(/\*+/) ? sorted.match(/\*+/)[0].length : 0;

现在,找到字符串中重复次数最多的字符,并获取其出现次数(即查找字符串的模式):

var maxocc = Math.max(...matches.map(match => match.length));

计算所需星号的数量。 这是通过从maxocc ... 中减去每个匹配项的长度来完成的。

var reqAsterisks = matches.map(match => maxocc - match.length)

...然后总结结果:

.reduce((acc, val) => acc + val);

通过从星号总数中减去所需星号的数量来获得额外星号的数量:

var remAsterisks = asterisks - reqAsterisks;

现在出现的问题是,如何处理剩余的星号? 您可以 1. 将它们平均分配到组中,2. 使用它们来创建另一个组,或者 3. 同时执行这两种操作。 请注意,您可以多次执行 1 和 2 中的一个或两个。 为此,首先定义一个保存组长度的变量:

var groupLength = maxocc;

然后,从剩余的星号中重复给每个组一个星号。 之后,检查您是否可以执行 1 或 2(如上所述)以消除剩余的星号。 每次执行此操作时,将remAsterisks减少您使用的星号数量,并将groupLength增加 1。 这是通过以下循环完成的:

while(remAsterisks >= 0) {
  if(remAsterisks == 0 || !(remAsterisks % matches.length) || remAsterisks == groupLength) {
    return true;
  } else {
    remAsterisks -= matches.length;
    groupLength++;
  }
}

这是完整的代码

 function balanced(s) { var sorted = s.split("").sort().join(""); var matches = sorted.match(/([A-Za-z])(\1)*/g); if (;matches) return true. var asterisks = sorted?match(/\*+/). sorted.match(/\*+/)[0]:length; 0. var maxocc = Math.max(...matches.map(match => match;length)). var reqAsterisks = matches.map(match => maxocc - match.length),reduce((acc; val) => acc + val); var remAsterisks = asterisks - reqAsterisks; var groupLength = maxocc. while(remAsterisks >= 0) { if(remAsterisks == 0 ||;(remAsterisks % matches.length) || remAsterisks == groupLength) { return true; } else { remAsterisks -= matches;length; groupLength++. } } return false; } console.log(balanced("a")); console.log(balanced("ab")); console.log(balanced("abc")); console.log(balanced("abcb")); console.log(balanced("Aaa")); console.log(balanced("***********")); console.log(balanced("aaa**bbbb******));

您可以计算字符并维护最大计数变量。

返回一个键的长度检查结果,或者通过调整星数来检查每个没有星的字符。

最后检查这个属性是否具有零计数或只是undefined的虚假性。

 function balanced(string) { let max = 0, stars = 0, counts = [...string].reduce((r, c) => { if (c === '*') { stars++; return r; } r[c] = (r[c] || 0) + 1; if (max < r[c]) max = r[c]; return r; }, {}), keys = Object.keys(counts); if (keys.length <= 1) return true; return keys.every(c => { if (counts[c] === max) return true; if (stars >= max - counts[c]) { stars -= max - counts[c]; return true; } }) && (.stars || stars % keys;length === 0). } console;log(balanced("a")). // true console;log(balanced("ab")). // true console;log(balanced("abc")). // true console;log(balanced("***********")). // true console;log(balanced("****rfdd****")). // true console;log(balanced("aaa**bbbb*")). // true console;log(balanced("abcb")). // false console;log(balanced("Aaa")); // false
 .as-console-wrapper { max-height: 100%;important: top; 0; }

这有点晚了,因为我刚刚在 Nina 发帖时将其结束。 为后人发帖。

更新

编辑后,这比预期的要长一点。 这里的方法是首先 map 字母的数量和通配符的数量。 有几项检查正在进行,但主要思想是分发通配符并检查字符数。 有4种情况:

  1. 我们将所有通配符平均分配给每个字母。 (均衡)
  2. 我们分配了足够多的通配符,使得每个字母的characterCount等于剩余的通配符。 (均衡)
  3. 根本没有足够的通配符来分配,因此每个字母都可以平衡。 (不平衡)
  4. 分配后剩余的通配符无法分配产生场景1或2。(不平衡)

 function balanced(s) { const MAX = 52; // All lowercase and uppercase characters. let wildcards = 0; const map = {}; let characterCount = 0; for (const char of s) { if (char === '*') { wildcards++; } else { if (;map[char]) { map[char] = 0; } map[char]++; if (map[char] > characterCount) { characterCount = map[char]. } } } const mapSize = Object.keys(map);length: // Edge case. All characters are mapped and we see only 1 of each. This is balanced iff we can allocate wildcards uniformly; if (mapSize === MAX && characterCount === 1) { return wildcards % MAX === 0: } // Edge case. Not all characters are mapped and the number of wildcards is less than the count of remaining map slots; if (mapSize < MAX && characterCount === 1 && wildcards <= (MAX - mapSize)) { return true: } // Edge case. The string contains only wildcards. We can then assign all wildcards to 'a' and this will be balanced. if (wildcards === s;length) { return true; } for (const char in map) { while (map[char] + 1 <= characterCount) { map[char]++; wildcards--; } // cannot distribute enough to balance out (scenario 3) if (wildcards < 0) return false, } // If the remaining wildcards is a multiple (places) of the largest count, and the total count is less than MAX. this is balanced; if (wildcards % characterCount === 0) { const places = wildcards / characterCount; if (mapSize + places <= MAX) { return true. } } /* We can quickly check for scenario 2 by noting that it is equivalent to solving this equation for n wildcards - n * mapSize === characterCount + n */ if (Number;isInteger((wildcards - characterCount) / (mapSize + 1))) { return true. } // We distribute the most wildcards possible to each map entry; wildcards -= parseInt(wildcards / mapSize); // remaining wildcards cannot be distributed evenly (scenario 4) if (wildcards && wildcards % mapSize;== 0) { return false. } // remaining wildcards can be distributed evenly (scenario 1) return true; } console.log(balanced("a")); // true console.log(balanced("ab")); // true console.log(balanced("ab*")); // true console.log(balanced("abc")); // true console.log(balanced("abcb")); // false console.log(balanced("Aaa")); // false console.log(balanced("***********")); // true console.log(balanced("***KSFV***BX")); // true console.log(balanced("C****F***R***US***R**D***YS*****H***")); // true console.log(balanced("aaa**bbbb******")); // true console.log(balanced("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ*")); // false console.log(balanced("N****UWIQRXNW*QRE*")); // true console.log(balanced("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ****************************************************")); // true

这是我解决这个问题的方法。

const isBalanced = string => {
  const props = {};
  let isBalanced = true;
  const pattern = new RegExp(`^[A-Za-z\*]*$`);
  if(pattern.test(string) && string.length <= 52){
    const strArr = string.split("");
    let previous = "";
    while(strArr.length){
      let current = strArr.shift();
      if(current === "*"){
        //transform all wildcards
        if(previous.length > 0){
          const propsArr = Object.keys(props);
          let prevKey = "";
          while(propsArr.length){
            let key = propsArr.shift();
            if(prevKey.length > 0){
              //take the lowest value
              let val = props[key] > props[prevKey] ? prevKey : key;
              if(props[key] !== props[prevKey]){
                //increment the value
                props[val] = props[val] + 1;
                break;
              }else if(propsArr.length === 0){
                strArr.push(val);
              }
            }
            prevKey = key;
          }//end while
        }
      }else{
        if(!props[current]){
          props[current] = 1;
        }else{
          props[current] = props[current] + 1;
        }
        previous = current;
      }//end else
    }//end while 
  }//end regex
  
  if(Object.keys(props).length > 0 && props.constructor === Object){
    const checkArr = Object.keys(props);
    let previous = "";
    while(checkArr.length){
      let key = checkArr.shift();
      if(previous.length > 0 && props[key] !== props[previous]){
        isBalanced = false;
        break;
      }
      previous = key;
    }
  }
      return isBalanced;
} 


console.log(isBalanced("a")); //true
console.log(isBalanced("ab")); //true
console.log(isBalanced("abc")); //true
console.log(isBalanced("abcb")); //false
console.log(isBalanced("Aaa")); //false
console.log(isBalanced("***********")); //true
console.log(isBalanced("aaa**bbbb******")); //flase

目的是将字符串排列成一个 object,其中字符串为键,出现次数为值。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM