【问题标题】:Check if a permutation of a string can become a palindrome检查字符串的排列是否可以成为回文
【发布时间】:2015-09-22 08:20:11
【问题描述】:

编写一个方法来测试一个字符串是否满足成为回文的前提条件。

例如:

Input    | Output
mmo      | True  
yakak    | True  
travel   | False

我正在考虑这种方法:

  1. 为 T 的所有排列创建一个后缀树,使得 T$Reverse(T)#
  2. 检查同一节点的所有排列

我错过了什么吗?

【问题讨论】:

  • 您只需排列所有字符并检查每个结果序列是否其反转等于序列本身。
  • 你的意思是:字符串的排列是否是回文?恐怕标题和问题有错误。
  • 测试你根本不需要做排列。您只需要进行一些审核:如果字符串长度为奇数,则应该有一个且只有一个字符的总数为奇数;如果字符串长度是偶数,则字符串上所有字符的总数应该是偶数。如果是这样的话,你真的把这个问题复杂化了。
  • 所以基本上你想确定是否可以将输入字符串的字符重新排列成回文?如果是这样,那么答案是“是”,如果 1) 长度是偶数,并且每个唯一字符出现 2 的倍数,或 2) 长度是奇数,并且每个唯一字符出现除 1 之外的 2 的倍数。跨度>

标签: string algorithm palindrome


【解决方案1】:

您需要做的就是检查最多有一个字符出现奇数次。这是一个 Java 示例:

private static boolean canMakePalindrom(String s) {
    Map<Character, Integer> countChars = new HashMap<>();

    // Count the occurrences of each character
    for (char c : s.toCharArray()) {
        Integer count = countChars.get(c);
        if (count == null) {
            count = Integer.valueOf(1);
        } else {
            count = count + 1;
        }
        countChars.put(c, count);
    }

    boolean hasOdd = false;
    for (int count : countChars.values()) {
        if (count % 2 == 1) {
            if (hasOdd) {
                // Found two chars with odd counts - return false;
                return false;
            } else {
                // Found the first char with odd count
                hasOdd = true;
            }
        }
     }

     // Haven't found more than one char with an odd count
     return true;
}

EDIT4(是的 - 这些排序是有意义的,但按时间顺序编号):
上面的实现有一个内置的低效率。我不认为可以避免对字符串的第一次迭代,但是没有真正的理由来记录所有出现的次数——只跟踪那些奇数的次数就足够了。对于这个用例,跟踪我们遇到的每个字符(例如,Set)就足够了,并在我们再次遇到它时将其删除。在最坏的情况下,字符串中的所有字符都不同,性能是相当的,但在每个字符出现多次的常见情况下,这种实现提高了第二个循环的时间和内存复杂度(即现在简化为单一条件):

private static boolean canMakePalindrom(String s) {
    Set<Character> oddChars = new HashSet<>();

    // Go over the characters
    for (char c : s.toCharArray()) {
        // Record the encountered character:
        if (!oddChars.add(c)) {
            // If the char was already encountered, remove it - 
            // this is an even time we encounter it
            oddChars.remove(c);
        }
    }

    // Check the number of characters with odd counts:
    return oddChars.size() <= 1;
}

EDIT3(是的 - 这些排序是有意义的,但按时间顺序编号):
Java 8 提供了一个流畅的流式 API,可用于创建类似于以下 Python 单行代码的实现:

private static boolean canMakePalindrom(String s) {
    return s.chars()
            .boxed()
            .collect(Collectors.groupingBy(Function.identity(),
                                           Collectors.counting()))
            .values()
            .stream()
            .filter(p -> p % 2 == 1)
            .count() <= 1;
}

编辑:
Python 的内置函数和理解能力使得它太有吸引力了,以至于不发布这个单一的解决方案。它可能比前面提到的 Java 效率低,但非常优雅:

from collections import Counter

def canMakePalindrom(s):
    return len([v for v in Counter(s).values() if v % 2 == 1]) <= 1

EDIT2:
或者,@DSM 在 cmets 中提出的更清洁的方法:

from collections import Counter

def canMakePalindrom(s):
    return sum(v % 2 == 1 for v in Counter(s).values()) <= 1

【讨论】:

  • 'aa' 呢?这不是您的解决方案未检测到的回文吗?
  • @MAA 为什么您认为"aa" ins 没有被这些解决方案检测到?我已经针对"aa" 运行了所有五个,它们都返回true。
  • 你是对的。我在想没有任何字符出现奇数,但这不是问题。
【解决方案2】:

另一种方法不是计算每个字母出现的次数,而是跟踪一个字母出现的次数是奇数还是偶数。如果一个字母出现了偶数次,你不需要担心它,只需要跟踪一个集合中的奇数出现。在 Java 中:

public static boolean canMakePalindrome(String s) {
    Set<Character> oddLetters = new HashSet<>();
    for ( char c : s.toCharArray() ) {
        if ( ! oddLetters.remove(c) ) {
            oddLetters.add(c);
        }
    }
    return oddLetters.size() <= 1;
}

【讨论】:

    【解决方案3】:

    您真正要寻找的只是所有(或除一个以外的所有)字母是否都配对。只要存在,就可以化作回文。

    所以它会像......

    bool canBeTurnedIntoAPalindrome(string drome)
    {
      // If we've found a letter that has no match, the center letter.
      bool centerUsed = false;
      char center;
    
      char c;
      int count = 0;
    
      // TODO: Remove whitespace from the string.
    
      // Check each letter to see if there's an even number of it.
      for(int i = 0; i<drome.length(); i++)
      {
        c = drome[i];
        count = 0;
    
        for(int j = 0; j < drome.length(); j++)
          if (drome[j] == c)
             count++;
    
        // If there was an odd number of those entries
        // and the center is already used, then a palindrome
        // is impossible, so return false.
        if (count % 2 == 1)
        {
          if (centerUsed == true && center != c)
            return false;
          else
          {
            centerused = true;
            center = c;   // This is so when we encounter it again it
                          // doesn't count it as another separate center.
          }
        }
      }
      // If we made it all the way through that loop without returning false, then
      return true;
    }
    

    这不是最有效的(它计算字母的次数与遇到字母的次数一样多,即使它们已经被计算过)但它确实有效。

    【讨论】:

      【解决方案4】:

      如果我正确理解了您的问题,我就是这样理解的:

      如果输入的字符串可以重新排列成回文,则输出“True”,否则输出“False”。

      那么你可以使用这些简单的规则:

      1. 如果长度是偶数,则输入中的每个唯一字符都必须出现 2 次。
      2. 如果长度为奇数,则除一个之外的每个唯一字符都必须出现 2 次的倍数。只允许 1 个字符出现 2 次的倍数。

      所以对于给定的 3 个示例:

      “mmo”,奇数长度,m 出现两次(2 的倍数),o 出现一次(不是 2 的倍数),所以 True

      “yakak”,奇数长度,a 出现两次(2 的倍数),k 出现两次(2 的倍数),y 出现一次(不是 2 的倍数),所以True

      “旅行”,超过一个字符不会出现2的倍数,所以False

      其他示例:

      “mmorpg”,只有m出现2的倍数,其余只出现一次,所以False

      “mmom”,没有字符出现2的倍数,多个字符出现“不是2的倍数”,所以False

      此时您应该意识到,如果只允许 1 个字符出现非 2 次,那么您可以忽略长度。具有偶数长度的字符串将有 2 个或更多字符出现非 2 次,或者根本没有出现。

      所以最后的规则应该是这样的:

      如果输入中最多有 1 个唯一字符出现非 2 倍,则输出为 True,否则输出为 False

      【讨论】:

        【解决方案5】:
        def can_permutation_palindrome(s):
            counter = {}
            for c in s:
                counter[c] = counter.get(c, 0) + 1
            odd_count = 0
            for count in counter.values():
                odd_count += count % 2
            return odd_count in [0, 1]
        

        【讨论】:

        • 虽然这段代码 sn-p 可以解决问题,但including an explanation 确实有助于提高帖子的质量。请记住,您正在为将来的读者回答问题,而这些人可能不知道您的代码建议的原因。也请尽量不要用解释性的 cmets 挤满你的代码,这会降低代码和解释的可读性!
        • Python 方法将受益于使用collections.Counter,这基本上就是这样做的。
        【解决方案6】:

        我今天达到了下面的解决方案(python)。我认为它的可读性和性能非常好。

        sum(map(lambda x: word.count(x) % 2, set(word))) <= 1
        

        我们基本上是在计算字符串“word”中每个字符出现的次数,得到除以 2 的余数,将它们全部相加并检查是否最多有 1 个。

        这个想法是您需要将所有字符配对,除了可能的一个(中间一个)。

        【讨论】:

          【解决方案7】:
          def check(string):
              bv = 0
              for s in string:
                  bv ^= 1 << ord(s)
              return bv == 0 or bv & (bv - 1) == 0
          

          【讨论】:

            【解决方案8】:

            我的想法是,如果奇数字母的数量是 1,其余的都是偶数,那么回文是可能的..这是我在 Python 中的程序

            string = raw_input()
            
            found = False
            char_set = set(string) # Lets find unique letters
            
            d_dict = {}
            for c in char_set:
                d_dict[c] = string.count(c) # Keep count of each letter
            
            odd_l = [e for e in d_dict.values() if e%2 == 1] # Check how many has odd number of occurrence     
            if len(odd_l) >1:
                pass
            else:
                found = True
            
            
            
            if not found:
                print("NO")
            else:
                print("YES")
            

            【讨论】:

              【解决方案9】:

              任何字符串只有在最多一个字符出现奇数时才能成为回文。次和所有其他字符必须出现偶数次。下面的程序可以用来检查回文是否可以是字符串。

              void checkPalindrome(string s)
              {
              vector<int> vec(256,0);    //Vector for all ASCII characters present.
              for(int i=0;i<s.length();++i)
              {
                  vec[s[i]-'a']++;
              }
              int odd_count=0,flag=0;
              for(int i=0;i<vec.size();++i)
              {
                  if(vec[i]%2!=0)
                      odd_count++;
                  if(odd_count>1)
                  {
                      flag=1;
                       cout<<"Can't be palindrome"<<endl;
                      break;  
                  }
              }
              if(flag==0)
                  cout<<"Yes can be palindrome"<<endl;
              }
              

              【讨论】:

                【解决方案10】:

                O(n) 复杂度。

                using System;
                using System.Collections.Generic;
                using System.Linq;
                using System.Text;
                using System.Threading.Tasks;
                
                namespace PallindromePemutation
                {
                    class charcount
                    {
                        public char character { get; set; }
                        public int occurences { get; set; }
                    }
                    class Program
                    {
                        static void Main(string[] args)
                        {
                
                            List<charcount> list = new List<charcount>();
                            charcount ch;
                            int count = 0;
                            char[] arr = "travel".ToCharArray();
                            for (int i = 0; i < arr.Length; i++)
                            {
                                charcount res = list.Find(x => x.character == arr.ElementAt(i));
                                if (res == null)
                                {
                                    ch = new charcount();
                                    ch.character = arr.ElementAt(i);
                                    ch.occurences = 1;
                                    list.Add(ch);
                                }
                                else
                                {
                                    charcount temp=  list.Find(x => x.character == arr.ElementAt(i));
                                    temp.occurences++;
                                }
                            }
                            foreach (var item in list)
                            {
                                if (!(item.occurences % 2 == 0))
                                {
                                    count++;
                                }
                            }
                            if (count > 1)
                            {
                                Console.WriteLine("false");
                            }
                            else
                            {
                                Console.WriteLine("true");
                            }
                            Console.ReadKey();
                        }
                    }
                }
                

                【讨论】:

                  【解决方案11】:

                  如果我们不关心字符串中字符和空格的大小写敏感性,那么在 C# 中使用 Dictionary 的示例解决方案可以是:

                      private static bool IsPalindromePermutation(string inputStr)
                      {
                          // First, check whether input string is null or whitespace.
                          // If yes, then return false.
                          if (string.IsNullOrWhiteSpace(inputStr))
                              return false;
                  
                          var inputDict = new Dictionary<char, int>();
                  
                          // Big/small letter is not important
                          var lowerInputStr = inputStr.ToLower();
                  
                          // Fill input dictionary
                          // If hit a space, then skip it
                          for (var i = 0; i < lowerInputStr.Length; i++)
                          {
                              if (lowerInputStr[i] != ' ')
                              {
                                  if (inputDict.ContainsKey(lowerInputStr[i]))
                                      inputDict[lowerInputStr[i]] += 1;
                                  else
                                      inputDict.Add(lowerInputStr[i], 1);
                              }
                          }
                  
                          var countOdds = 0;
                          foreach(var elem in inputDict)
                          {
                              if(elem.Value % 2 != 0)
                                  countOdds++;
                          }
                  
                          return countOdds <= 1;
                      }
                  

                  【讨论】:

                    【解决方案12】:

                    我们也可以通过集合来实现这一点

                    String name = "raa";
                            List<Character> temp = new ArrayList<>(name.chars()
                                    .mapToObj(e -> (char) e).collect(Collectors.toList()));
                    
                            for (int i = 0; i < temp.size(); i++) {
                                for (int j = i + 1; j < temp.size(); j++) {
                                    if (temp.get(i).equals(temp.get(j))) {
                                        temp.remove(j);
                                        temp.remove(i);
                                        i--;
                                    }
                    
                                }
                    
                            }
                    
                            if (temp.size() <= 1) {
                                System.out.println("Pallindrome");
                            } else {
                                System.out.println(temp.size());
                                System.out.println("Not Pallindrome");
                            }
                        }
                    

                    【讨论】:

                      【解决方案13】:

                      这是我的解决方案

                      public static void main(String[] args) {
                          List<Character> characters = new ArrayList<>();
                          Scanner scanner = new Scanner(System.in);
                          String input = scanner.nextLine();
                          for (int i = 0; i < input.length(); i++){
                              char val = input.charAt(i);
                              if (characters.contains(val)){
                                  characters.remove(characters.indexOf(val));
                              } else{
                                  characters.add(val);
                              }
                          }
                          if (characters.size() == 1 || characters.size() == 0){
                              System.out.print("Yes");
                          } else{
                              System.out.print("No");
                          }
                      }
                      

                      【讨论】:

                      • 看起来像 Java,你可能会提到。看看Mureinik's 答案中的Edit 4 - 在我眼中只是缺少doc comment。不要写,永远不要发布未注释的代码!
                      【解决方案14】:

                      这就是我的解决方案。该字符串可以包含多个带有空格的单词,例如
                      输入:Tact Coa 输出真 输入:Tact Coa vvu 输出:假

                      public static boolean checkForPalindrome(String str) {
                          String strTrimmed = str.replaceAll(" ","");
                          System.out.println(strTrimmed);
                          char[] str1 = strTrimmed.toCharArray();
                      
                          for (int i = 0; i < str1.length; i++) {
                              str1[i] = Character.toLowerCase(str1[i]);
                          }
                      
                          Arrays.sort(str1);
                          String result = new String(str1);
                          System.out.println(result);
                          int count = 0;
                          for (int j = 0; j < str1.length; j += 2) {
                          if (j != str1.length-1) {
                              if (str1[j] != str1[j+1]) {
                                  count++;
                                  j++;
                      
                              }
                          } else {
                              count++;
                          }
                         }        
                          if (count > 1) return false;
                          else return true;
                      }
                      

                      【讨论】:

                        【解决方案15】:

                        问题:字符串可以变成回文吗? 方法1:字符数 在 Java 中:

                        public class TEST11 {
                        
                            public static void main(String[] args) {
                                String a = "Protijayi";
                        
                                int[] count = new int[256];
                                Arrays.fill(count, 0);
                                for (int i = 0; i < a.length(); i++) {
                                    char ch = a.charAt(i);
                                    count[ch]++;
                                } // for
                                    // counting of odd letters
                                int odd = 0;
                                for (int i = 0; i < count.length; i++) {
                                    if ((count[i] & 1) == 1) {
                                        odd++;
                                    }
                        
                                } // for
                                if (odd > 1) {
                                    System.out.println("no");
                                } else {
                                    System.out.println("yes");
                                }
                        
                            }
                        
                        }
                        

                        在 Python 中:

                        def fix (a):
                            count = [0] * 256
                            for i in a: count[ord(i)] += 1
                            # counting of odd characters
                            odd = 0 
                            for i in range(256): 
                                if((count[i] & 1) == 1): odd += 1
                        
                            if(odd > 1):print("no")
                            else:print("yes")
                        
                        
                        a = "Protijayi"
                        
                        fix(a)
                        

                        方法二:使用HashSet 在 Java 中:

                        public class TEST11 {
                        
                            public static void main(String[] args) {
                        
                                String a = "Protijayi";
                                Set<Character> set = new HashSet<>();
                                for (char ch : a.toCharArray()) {
                        
                                    if (set.contains(ch)) {
                                        set.remove(ch);
                                    }
                                    set.add(ch);
                                } // for
                        
                                if (set.size() <= 1) {
                                    System.out.println("yes can be a palindrome");
                                } else {
                                    System.out.println("no");
                                }
                        
                            }
                        
                        }
                        

                        【讨论】:

                          【解决方案16】:

                          这个问题的 Swift 示例。

                          var str = "mmoosl"
                          extension String {
                          func count(of needle: Character) -> Int {
                              return reduce(0) {
                                  $1 == needle ? $0 + 1 : $0
                              }
                          }
                          }
                          
                          
                          
                          func canBeTurnedIntoAPalinpolyString(_ polyString: String) -> Bool {
                          var centerUsed = false
                          var center = Character("a")
                          for i in polyString {
                              let count  = polyString.count(of: i)
                              if count == 1 && !centerUsed {
                                  center = i
                                  centerUsed = true
                              } else {
                                  if count % 2 != 0 {
                                      return false
                                  }
                              }
                          }
                           return true
                          }
                          
                          print(canBeTurnedIntoAPalinpolyString(str))
                          

                          【讨论】:

                            【解决方案17】:

                            Java

                            private static boolean isStringPalindromePermutation(String input) {
                            
                                if(input == null) return false;
                            
                                if(input.isEmpty()) return false;
                            
                                int checker = 0;
                                for (int i = 0; i < input.length(); i++) {
                                    int character = input.charAt(i) - 'a';
                            
                                    int oneShiftedByNumberInCharacter = 1 << character;
                            
                                    int summaryAnd = checker & oneShiftedByNumberInCharacter;
                                    if ( summaryAnd > 0 ) {
                                        int revertToShiftedByChar = ~oneShiftedByNumberInCharacter;
                                        checker = checker & revertToShiftedByChar;
                                    } else {
                                        checker |= oneShiftedByNumberInCharacter;
                                    }
                                }
                                if ( input.length() % 2 == 0 ) {
                                    if ( checker == 0) {
                                        return true;
                                    }
                                    else return false;
                                } else {
                                    int checkerMinusOne = checker-1;
                                    if((checkerMinusOne & checker) == 0){
                                        return true;
                                    }else{
                                        return false;
                                    }
                                }
                            
                            }
                            

                            【讨论】:

                              【解决方案18】:

                              为什么要使用后缀树或任何其他数据结构?

                              回文字符串的基本要求所有字符的频率 必须是偶数 或者只有一个字符可以是奇数频率

                              示例:-

                              输入 : aabbaa

                              输出:a 的频率为 4,b 的频率为 2(均为偶数)

                              输入:xxzyzxx

                              输出:x 的频率为 4,z 为 2,y=1(只有 1 个奇数)

                              示例代码便于理解:

                              bool ispalin(string str)                      //function to check
                              { 
                              int freq[26] = {0};               //to store frequency of character here i am
                                                                        // considering only lower case letters 
                              for (int i = 0; str.length();  i++) 
                                  freq[str[i]]++; 
                              int odd = 0; 
                              for (int i = 0; i < 26; i++)      //Count odd occurring characters 
                              { 
                                  if (freq[i] & 1)                       //checking if odd
                                      odd++; 
                                  if (odd > 1)                          //if number of odd freq is greater than 1
                                      return false; 
                              }  
                              return true;                             //else return true
                              } 
                              

                              【讨论】:

                                【解决方案19】:

                                python 代码检查是否可以从给定的字符串形成回文:

                                test_str = input('enter any string = ')
                                count = 0 
                                for item in set(test_str):
                                    if test_str.count(item)%2 != 0:
                                        count+=1 
                                if (count>1):
                                    print(" palindrome cannot be formed")
                                else:
                                    print(" palindrome can be formed")
                                

                                如果有任何问题请尝试此代码

                                【讨论】:

                                【解决方案20】:

                                更高效的实现 - Java

                                boolean palindromeRearranging(String inputString) {
                                    Map<Character, Integer> charsCount = new HashMap<Character, Integer>();
                                    for(char c : inputString.toCharArray()){
                                        charsCount.compute(c, (key, val) ->  val == null ? 1 : val + 1);
                                    }
                                    List<Integer> result = new ArrayList<>();
                                    charsCount.forEach((k, v) -> {
                                        if(v % 2 != 0){
                                            result.add(v);
                                        }
                                    });
                                    return (result.size() == 0 || result.size() == 1);
                                }
                                

                                【讨论】:

                                  【解决方案21】:

                                  这是我的代码:

                                   boolean palindromeRearranging(String inputString) {
                                          HashMap<Character,Integer> stCount=new HashMap<>();
                                          for(int i=0;i<inputString.length();i++){
                                              stCount.put(inputString.charAt(i),0);
                                          }
                                          for(int i=0;i<inputString.length();i++){
                                              int c= stCount.get(inputString.charAt(i));
                                              stCount.put(inputString.charAt(i),++c);
                                          }
                                          int c=0;
                                          for (Map.Entry<Character,Integer> entry : stCount.entrySet()){
                                              if(entry.getValue()%2!=0){
                                                  c++;
                                                  if(c>1){
                                                      return false;
                                                  }
                                              }   
                                          }
                                          return true;
                                      }
                                  

                                  【讨论】:

                                  • 您的答案可以通过额外的支持信息得到改进。请edit 添加更多详细信息,例如引用或文档,以便其他人可以确认您的答案是正确的。你可以找到更多关于如何写好答案的信息in the help center
                                  • 为了在 String 中迭代“字符”,我喜欢 for (int c: s.codepoints())。 “计数元素”的成语可能是stCount.merge(c, 1, Integer::sum)。当您不需要密钥时,只需迭代stCount.values()
                                  猜你喜欢
                                  • 2012-04-05
                                  • 1970-01-01
                                  • 1970-01-01
                                  • 1970-01-01
                                  相关资源
                                  最近更新 更多