字符串可以有回文吗,有没有更好的方法呢?

时间:2016-02-20 16:22:08

标签: javascript string algorithm

今天我有一个非常有趣的采访问题:

  

给定一个字符串,你必须确定该字符串在置换时是否可以有回文。

以下是我提出的实施方案。但是有更好的解决方案吗?

function canBePalindrome(someStr) {

    if (typeof someStr !== "string") {
      throw new Error("Expecting argument to be a string !");
    }

    if (someStr.length == 1) return someStr;
    var canBePalin = false;
    var _chunks = someStr.split("");
    var _length = _chunks.length;
    for (var i = 0; i < _length; i++) {
      for (var j = i + 1; j < _length; j++) {
        var temp_char = _chunks[i];
        _chunks[i] = _chunks[j];
        _chunks[j] = temp_char;

        if (isPalindrome(_chunks.join(""))) return true;

      }
    }
    return canBePalin;

  } //End of canBePalindrome


function isPalindrome(someStr) {
    //console.log("Checking for:"+someStr);
    var original = someStr.split("");
    return someStr === original.reverse().join("");
  } //End of isPalindrome

canBePalindrome("mdadm");

这不可能重复,因为我不是直接检查它是否是回文,而是检查和检查它。

6 个答案:

答案 0 :(得分:6)

保留一个字符地图,计算它们,并查看所有字符的计数是否均匀,如果是,则可以创建回文

function canBePalindrome(someStr) {
    var map = {};
    var arr = someStr.split('');

    arr.forEach(function(s) {
        s in map ? map[s]++ : map[s] = 1;
    });

    var len = Object.keys(map).filter(function(o) {
        return map[o] % 2;
    }).length;

    return arr.length % 2 ? len === 1 : len === 0;
}

FIDDLE

以上的“高尔夫”版本将是

function canBePalindrome(someStr) {
    return (function(m, a, l) {
        a.forEach(function(s) { s in m ? m[s]++ : m[s] = 1 });  
        l = Object.keys(m).filter(function(o) { return m[o] % 2 }).length;
        return a.length % 2 ? l === 1 : l === 0;
    })({}, someStr.split(''));
}

答案 1 :(得分:3)

如果字符串中的所有字母都具有偶数,除了最多一个,您始终可以将它们重新组织成回文。我的解决方案比其他解决方案稍长,因为我试图最小化带有函数和不必要的数组创建的循环的性能开销。

aaaabbbb => aabbbbaa
aaaabbbbc => aabbcbbaa


function isPalindromable(str) {
    var map = getCharCount(str);
    var nonPairs = 0;
    for (var char in charMap) {
        if (charMap[char] % 2 != 0)
            nonPairs++;
        if (nonPairs > 1)
            return false;
    }
    return true;
}

function getCharCount(str) {
    // Number of times each string appeared
    var map = {};
    for (var i = 0; i < str.length; i++) {
        var ch = str.charAt(i);
        map[ch] = ++map[ch] || 1;
    }
    return map;
}

如果您喜欢一个衬垫(不是那么快,而是优雅)

function isPalindromable(str) {
    var charMap = getCharCountMap(str);
    return Object.keys(charMap).filter(char => charMap[char] %2 > 0).length <= 1;
}

function getCharCountMap(str) {
    return str.split('').reduce((prev, cur) => (prev[cur] = ++prev[cur] || 1, prev) , {})
}

效果编辑

我对三种解决方案进行了基准测试。当琴弦很短时,我的速度最慢,而对于较长的琴弦,最快。然而,一位工作的朋友想出了一个最快速的解决方案,可能是因为它只有一个循环但不需要排序。 http://jsperf.com/can-string-be-made-into-palindrome/2

function canBePalindromeReplace(string) {
    var oddCount = 0;

    while(string.length > 0) {
        var char = string.charAt(0);
        var stringLength = string.length;
        string = string.replace(new RegExp(char, 'g'), '');
        var diff = stringLength - string.length;
        if((diff % 2) != 0) {
            oddCount++;
            if(oddCount > 1) {
                return false;
            }
        }
    }

    return true;
}

答案 2 :(得分:1)

使用排序和堆栈的解决方案:

function canBePalindrome(someStr) {
    var stack = [];
    someStr.split('').sort().forEach(function(ch) {
        stack[stack.length - 1] === ch ? stack.pop() : stack.push(ch);
    });
    return stack.length < 2;
}

答案 3 :(得分:0)

一个很好的技巧是看到回文的以下属性:

  • 当字符串的大小成对时,所有字符应该出现一对次。
  • 当字符串的大小为奇数时,所有字符但一个应该出现一对次。

使用此信息,您可以编写一个算法来计算字符串中每个可能的char出现的次数,然后查看是否符合以前的属性。此算法应具有O(N + M)时间复杂度,其中N是字符串的大小,M是字符串中可能出现的字符数量。例如,如果字符串只有ASCII字符,则时间复杂度为O(N + 255)。空间复杂度为O(N + M)

答案 4 :(得分:0)

我认为更好的方法是创建一个长度为26的数组arr[26]。每个字母一个。然后通过字符串增加每个字母发生的频率。

完成后,请查看arr[]并检查所有频率是否均匀。只有一个奇数频率的字符,我们可以把它放在回文的中间。

如果所有频率均匀,那么它可以组织成回文。

否则不是。

算法看起来应该是这样的

str[] holding the string;
create arr[26] and intialize with 0;

for char in str:
    arr[char]++  //char should be mapped to its index properly
                 // like a = 0, b = 1 and so on

no_of_odd_char = 0
for freq in arr:
    if(freq % 2 == 1)
        if(no_of_odd_char == 1)
            print "cannot be palindrome"
            exit()
        else if(no_of_odd_char == 0)
            no_of_odd_char = 1

print "can be palindrome"

答案 5 :(得分:0)

如果出现奇数次的字符数为0或1,则存在字符串的排列。

int main() {
    int* p;             // p is at address #1 and the value uninitialized. p will
                        // be at address #1 throughout the whole program.
                        // It is only the value of p that can change.

    p = new int(6);     // The value of p is set to address #2 and your
                        // program now has ownership of sizeof(int) bytes
                        // starting from address #2. The int at address #2
                        // is initialized with the value 6.
    cout << *p << endl;

    delete p;           // Your program releases ownership of the memory at
                        // address #2 and you are not allowed to use it anymore.
                        // The value of p is undefined.

    return 0;
}

您可以使用function canBePalindrome(str) { var isOdd = Object.create(null), numOdd = 0; for(let ch of str) numOdd += (isOdd[ch] = !isOdd[ch]) * 2 - 1; return numOdd <= 1; } 进行迭代以支持ES6之前的实现,但如果字符串包含超出代码点范围前16位的unicode代码点,则无法正常工作。