递增一个包含字母的字符串?

13

我需要将一个字符串从...比如说aaa,一直增加到zzz,并且把每次增加的结果输出到控制台("incrementation"这个词是否正确?)。大概是这样的:

aaa
aab
aac
...
aaz

aba
abb
abc
...
abz

aca
acb

等等,到目前为止,我通过这种方式增加了一个单词中的字母:

String.prototype.replaceAt = function(index, character) {
    return this.substr(0, index) + character + this.substr(index+character.length);
}

string = "aaa";

string = string.replaceAt(2, String.fromCharCode(string.charCodeAt(2) + 1));

//string == "aab"

然而,当最后一个字母为z时,我就无法继续了,应该将第二个字母(索引1)增加并重新设置最后一个字母为a

有没有人知道或者想到一个巧妙的解决方法呢?谢谢!


它需要区分大小写吗? - Himanshu Tanwar
以下答案比被接受的答案快两倍,jsPerf提供了证明:http://stackoverflow.com/a/30687539/1636522 :-) - user1636522
这是一个用于 Node.js 的模块:https://www.npmjs.com/package/incstr - grabantot
12个回答

27

将字符串视为一个36进制数。

转换为十进制,加1,再转换回36进制,并将任何零替换为字母'a':

将字符串看作36进制数字。

将其转换为10进制,加1,然后再转换回36进制。最后用字母'a'替换所有的零:

var str= 'aaa',
    s= str;

while(str!=='zzz') {
  str= ((parseInt(str, 36)+1).toString(36)).replace(/0/g,'a');
  s+= ' '+str;
}

document.body.innerHTML= s;


迄今为止最佳答案。谢谢。 - MortenMoulder
1
使用36进制数字系统非常棒 :) - fsacer
2
谢谢。真正的诀窍是处理数字0-9,直到我意识到由于我们按1递增,只有数字0会出现。用字母'a'替换即可解决这个问题。 - Rick Hitchcock
为了更通用,您可以使用此方法生成一串z的字符串:https://dev59.com/nWYq5IYBdhLWcg3woB0W - fsacer
谢谢@fsacer,我有时会使用“new Array”技巧。你也可以循环直到字符串的长度发生变化。 - Rick Hitchcock
2
在“s+= ' '+str;”之前添加str = str.replace(/1/g,'a');以实现动态长度。 - Moshe Quantz

10

这个函数根据一个数字返回3个字符:

function n2s (n) {
    var s = '';
    while (s.length < 3) {
        s = String.fromCharCode(97 + n % 26) + s;
        n = Math.floor(n / 26);
    }
    return s;
}

打印从“aaa”到“zzz”的字符串:

var zzz = Math.pow(26, 3) - 1;
for (var n = 0; n <= zzz; n++) {
    console.log(n2s(n));
}

function n2s (n) {
    var s = '';
    while (s.length < 3) {
        s = String.fromCharCode(97 + n % 26) + s;
        n = Math.floor(n / 26);
    }
    return s;
}

var result = [];
var zzz = Math.pow(26, 3) - 1;
for (var n = 0; n <= zzz; n++) {
    result.push(n2s(n));
}
document.body.innerHTML = result.join(' ');

请求细节:-)


改进

接受的答案相比,性能提升:http://jsperf.com/10-to-26

// string to number: s2n("ba") -> 26
function s2n(s) {
    var pow, n = 0, i = 0;
    while (i++ < s.length) {
        pow = Math.pow(26, s.length - i);
        n += (s.charCodeAt(i - 1) - 97) * pow;
    }
    return n;
}

// number to string: n2s(26) -> "ba"
function n2s(n) {
    var s = '';
    if (!n) s = 'a'; 
    else while (n) {
        s = String.fromCharCode(97 + n % 26) + s;
        n = Math.floor(n / 26);
    }
    return s;
}

// pad("ba", 4) -> "aaba"
function pad (s, n) {
    while (s.length < n) s = 'a' + s;
    return s;
}

使用方法:

var from = s2n('azx');
var to = s2n('baa');
for (var n = from; n <= to; n++) {
    console.log(pad(n2s(n), 3));
}

输出:

azx
azy
azz
baa

递归

可能会在内存使用或计算时间方面效率较低:https://jsperf.com/10-to-26/4

function n2s(n) {
    var next = Math.floor(n / 26);
    return (
        next ? n2s(next) : ''
    ) + (
        String.fromCharCode(97 + n % 26)
    );
}

function s2n(s) {
    return s.length && (
        (s.charCodeAt(0) - 97)
    ) * (
        Math.pow(26, s.length - 1)
    ) + (
        s2n(s.slice(1))
    );
}

2
那个回答值得更多的赞同。它比被接受的答案更加通用。在改变一些硬编码的数字后,适用于任何字母表。 - grabantot
n2s(n) 中获取 aaa 是否可能? - Bouh
1
@bouh 我不这么认为,因为 a 代表的是 0,但你可以使用 pad 函数来实现,像这样:pad("", 3) - user1636522

4

采用了一种算法方法。该函数将初始字符串作为参数,递增下一个可能的字母,并最后返回结果。

function generate(str)
{
  var alphabet = 'abcdefghijklmnopqrstuvwxyz'.split('');
  var chars = [];
  for(var i = 0; i < str.length; i++)
  {
    chars.push(alphabet.indexOf(str[i]));
  }
  for(var i = chars.length - 1; i >= 0 ; i--)
  {
    var tmp = chars[i];
    if(tmp >= 0 && tmp < 25) {
      chars[i]++;
      break;
    }
    else{chars[i] = 0;}
  }
  var newstr = '';
  for(var i = 0; i < chars.length; i++)
  {
    newstr += alphabet[chars[i]];
  }
  return newstr;
} 

这是一个循环辅助函数,它接受要循环的初始字符串并生成所有的组合。

function loop(init){
  var temp = init;
  document.write(init + "<br>");
  while(true)
  {
    temp = generate(temp);
    if(temp == init) break;
    document.write(temp + "<br>");
  }
}

使用方法: loop("aaa");

CODEPEN


那么我该如何从aaa变成zzz并将每个结果输出到控制台? - MortenMoulder
我不认为我理解了。请更新你的答案。 - MortenMoulder
也可以运行,但我选择了另一个答案 :) - MortenMoulder
是的,那个可能更有效和聪明。唯一缺少的是生成相等长度的z字符串。 - fsacer

2
我采用了不同的方法,使用排列函数递归生成所有可能的排列,其中使用了重复n次的数组中的字符。代码如下。

//recursively generates permutations
var permutations = function (li, rep) {
    var i, j, next, ret = [];
    // base cases
    if (rep === 1) {
        return li;
    }
    if (rep <= 0) {
        return [];
    }
    // non-base case
    for (i = 0; i < li.length; i += 1) {
        // generate the next deepest permutation and add
        // the possible beginnings to those
        next = permutations(li, rep-1);
        for (j = 0; j < next.length; j += 1) {
            ret.push(li[i] + next[j]);
        }
    }
    return ret;
};

// returns an array of numbers from [start, end)
// range(10, 14) -> [10, 11, 12, 13]
var range = function (start, end) {
    var i, ret = [];
    for (i = start; i < end; i+= 1) {
        ret.push(i);
    }
    return ret;
};

// generates letters ('abcd...')
var letters = String.fromCharCode.apply(this, range('a'.charCodeAt(0), 'z'.charCodeAt(0)+1));

// calls the function itself, and .join's it into a string
document.body.innerHTML = (permutations(letters, 3)).join(' ');


1

我使用了你的代码并添加了一些新功能。

String.prototype.replaceAt = function(index, character) {
    return this.substr(0, index) + character + this.substr(index+character.length);
}

String.prototype.incrementAt = function(index) {
    var newChar = String.fromCharCode(this.charCodeAt(index) + 1); // Get the next letter that this char will be
    if (newChar == "{") { // If it overflows
        return this.incrementAt(index - 1).replaceAt(index, "a"); // Then, increment the next character and replace current char with 'a'
    }
    return this.replaceAt(index, newChar); // Replace this char with next letter
}

String.prototype.increment = function() {
    return this.incrementAt(this.length - 1); // Starts the recursive function from the right
}

console.log("aaa".increment()); // Logs "aab"
console.log("aaz".increment()); // Logs "aba"
console.log("aba".increment()); // Logs "abb"
console.log("azz".increment()); // Logs "baa"

这个 incrementAt 函数是递归的,它会增加它当前所在的字符。如果在此过程中发生溢出(字符变成了{,即紧随其后的是z),它将调用位于当前字符之前的字母上的incrementAt
这段代码唯一的问题是,如果你尝试增加zzz,你会得到aaaz。这是因为它试图增加第-1个字符,也就是最后一个字符。如果我有时间,稍后我会更新我的答案以解决这个问题。
请注意,如果你有一个不同长度的字符串作为起点,这个解决方案也会奏效。例如,“aaaa”可以很好地计数到“zzzz”。

那么我应该如何从aaa到zzz并对每个字符串执行某些操作呢?比如说,如果我想要在控制台中输出从aaa到zzz的每一种方式(或者为了简单起见,从aa到zz)? - MortenMoulder
所以,你只需要像这样说:myStr = "aa"; while (myStr!= "zz") { myStr = myStr.increment(); console.log(myStr); } 这将为您提供从 aa 到 zz 的结果。 - Will

1

让我们尝试这种方法。这是一个直接的循环,它从aaa,aab,aac,.....,xzz,yzz,zzz生成完整的序列。

function printSeq(seq){
    console.log(seq.map(String.fromCharCode).join(''));
}


var sequences = [];

(function runSequence(){
    var seq = 'aaa'.split('').map(function(s){return s.charCodeAt(0)});
    var stopCode = 'z'.charCodeAt(0);
    do{
        printSeq(seq);
        sequences.push(seq.map(String.fromCharCode).join(''));
        if (seq[2]!=stopCode) seq[2]++;
        else if (seq[1]!=stopCode) seq[1]++;
        else if (seq[0]!=stopCode) seq[0]++;
    }while (seq[0]<stopCode);
    printSeq(seq);
    sequences.push(seq.map(String.fromCharCode).join(''));
})();

结果将显示在控制台中,同时您还将获得完整的序列存储在sequence数组中。希望这篇文章易读且有帮助。

你测试过代码了吗?我从aaa到aaz,结果一团糟。 - MortenMoulder
肯定的是,在回复之前我重新运行了这个片段,得到了正确的结果。输出从aaa、aab、...zzz运行。你是在StackOverflow片段中运行它还是复制到其他地方运行的? - TaoPR
我在JSFiddle中尝试过了。感谢您的贡献,但我已经标记了一个作为答案。 - MortenMoulder
@Snorlax 不用担心。我也很喜欢你选择的答案。我只是想提供一种不同的方法 :) - TaoPR

1
我想提供一个替代方案来回答@procrastinator(因为我没有足够的Stackoverflow积分无法在其回答下评论)。他的回答似乎是最通用的方法,但我注意到,在“z”之后是“ba”,而op期望是“aa”。此外,这也符合Excel列名的命名方式。
这是已更正的代码:
function s2n(s) {
    var pow, n = 0, i = 0;
    while (i++ < s.length) {
        pow = Math.pow(26, s.length - i);
        var charCode = s.charCodeAt(i - 1) - 96;
        n += charCode * pow;
    }
    return n;
}

function n2s(n) {
    var s = '';  
    var reduce = false;

    if (n === undefined) {
        n = 0;
    } else {
        n--;
    }
    while (n !== undefined) {
        s = String.fromCharCode(97 + n % 26) + s;
        n = Math.floor(n / 26);
        if (n === 0) {
            n = undefined;
        } else {
            n--;
        }
    }
    return s;
}

这里不是从0开始计数,而是将1看作“a”,26看作“z”,27看作“aa”等等。


0

假设您始终拥有3个字母(或任何其他固定数量的字母),我会想到:

为每个字母设置单独的变量,而不是:

string = "aaa";

有:

string1 = "a";
string2 = "a";
string3 = "a";

然后在每次迭代中递增所需的那个。这可能需要一些试错,看起来你是从右到左进行的,因此大致如下:

if(string3 != "z"){
    // Increment string 3 by a letter
}else if(string2 != "z"){
    // Increment string 2 by a letter
}else if (string1 != "z"){
    // Increment string 1 by a letter
}else{
    // What ever you want to do if "zzz"
}

我没有测试过,但应该会很接近。

然后

string = string1 + string2+ string3

现在你只剩下一个变量,就像之前一样,你可以按照你的意图进行操作(例如输出等)。

你也可以使用字符串数组来实现这个功能,这样可以更容易地拥有不同数量的字母,并且需要一些代码来计算数组长度等等,但我希望先像上面那样静态地让它工作起来。


我明白你的意思,但这不是正确的方法。当然它可能会起作用,但我的三个字母的例子只是一个例子。 - MortenMoulder

0

使用Number#toString的有趣方法:

var n = 13330
var ns = []

for(var i = 0; i < 26; i++) {
  for(var j = 0; j < 26; j++) {
    for(var k = 0; k < 26; k++) {
      ns.push(n.toString(36))
      n++
    }
    n += 10 // jump from '(x)0' to '(x+1)a', etc.
  }
  n += 360 // jump from '(x)0a' to '(x)aa', etc.
}

console.log(ns) // the strings you wanted

0

这个函数将会执行将字符串递增到下一个序列的部分。

function increment(str){

    var arr = str.split("");
    var c;
    for(var i=arr.length-1; i>=0; i--){
        c = (arr[i].charCodeAt(0)+1)%123;
        arr[i] = String.fromCharCode(c==0?97:c);
        if(c!=0)break;
    }
return arr.join("");
}

我正在开发另一种解决方案,可以按任意数字递增或逆向递增。代码仍然存在一些错误,但是我在这里发布它以获得一些建议。传入负数以逆向递增。代码在某些边缘情况下会失败,例如:当字符为'a'且数字为负数时。

function jumpTo(str,num){

    var arr = str.split("");
    var c;
    for(var i=arr.length-1; i>=0; i--){
        c = (arr[i].charCodeAt(0)+1)%123;
        c += c==0?97+num-1:num-1;
        arr[i] = String.fromCharCode(c==0?97:c);
        if(c!=0)break;
    }
return arr.join("");
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接