检查字符串的所有字符是否包含相同的次数

时间:2018-10-01 02:35:23

标签: java algorithm data-structures hashmap

我正在研究以下问题陈述:

  

如果字符串的所有字符都显示相同,则该字符串有效   次数。如果我们只删除1个字符也是有效的   字符串中的1个索引,其余字符将出现   相同的次数。给定字符串s,确定它是否有效。如果   因此,返回YES,否则返回NO。

     

例如,如果s=abc,则它是有效的字符串,因为频率是   {a:1,b:1,c:1}s=abcc也是如此,因为我们可以删除一个c   在剩余的字符串中每个字符都有1个。如果s=abccc   但是,该字符串无效,因为我们只能删除1次出现的   c。这样将保留{a:1,b:1,c:2}的字符频率。

我想出了下面的代码,但是它没有按预期工作,并且在此输入abcdefghhgfedecba上失败。它正在打印“否”,但对于该输入应为“是”。

private static String isValid(String s) {
    if (s == null || s.equals("")) {
        return "NO";
    }
    Map<Character, Integer> frequencies = new HashMap<>();
    for (char ch : s.toLowerCase().toCharArray())
        frequencies.put(ch, frequencies.getOrDefault(ch, 0) + 1);

    int count = 0;
    // Iterating over values only
    for (Integer value : frequencies.values()) {
        if (value == 2) {
            count++;
        }
    }
    if (count >= 1) {
        return "YES";
    }
    return "NO";
}

我在这里做什么错?最佳和有效的方法是什么?

3 个答案:

答案 0 :(得分:1)

计算频率是正确的主意,尽管我不确定为什么要检查映射中的值是否为2。计算完这些频率后,我将创建具有每个频率的字符数的反向映射,然后:

  1. 如果地图的大小为1,则表示所有字符的频率相同-字符串有效。
  2. 如果集合的大小为2:
    • 如果最小频率为1,并且只有一个具有该频率的字符,则字符串是有效的,因为可以轻松删除该字符
    • 如果最小频率比最大频率小1,并且只有一个具有最大频率的字符,则字符串有效,因为可以删除此字符。
  3. 在其他情况下,该字符串将无效。


private static boolean isValid(String s) {
    TreeMap<Long, Long> frequencyCounts =
            s.chars()
             .boxed()
             // Frequency map
             .collect(Collectors.groupingBy(Function.identity(), Collectors.counting()))
             .values()
             .stream()
             // Frequency of frequencies map
             .collect(Collectors.groupingBy
                                 (Function.identity(),
                                  TreeMap::new,
                                  Collectors.counting()));

    if (frequencyCounts.size() == 1) {
        return true;
    }

    if (frequencyCounts.size() == 2) {
        Iterator<Map.Entry<Long, Long>> iter = frequencyCounts.entrySet().iterator();
        Map.Entry<Long, Long> minEntry = iter.next();
        long minFrequency = minEntry.getKey();
        long numMinFrequency = minEntry.getValue();

        if (minFrequency == 1L && numMinFrequency == 1L) {
            return true;
        }

        Map.Entry<Long, Long> maxEntry = iter.next();
        long maxFrequency = maxEntry.getKey();
        long numMaxFrequency = maxEntry.getValue();
        if (numMaxFrequency == 1L && maxFrequency == minFrequency + 1L) {
            return true;
        }
    }

    return false;
}

编辑:
为了回答评论中的问题,尽管可能不那么优雅,但也可以使用Java 7的语法构造频率图和“频率频率”图:

Map<Character, Long> frequencies = new HashMap<>();
for (int i = 0; i < s.length(); ++i) {
    char c = s.charAt(i);
    if (frequencies.containsKey(c)) {
        frequencies.put(c, frequencies.get(c) + 1L);
    } else {
        frequencies.put(c, 1L);
    }
}

TreeMap<Long, Long> frequencyCounts = new TreeMap<>();
for (Long freq : frequencies.values()) {
    if (frequencyCounts.containsKey(freq)) {
        frequencyCounts.put(freq, frequencyCounts.get(freq) + 1L);
    } else {
        frequencyCounts.put(freq, 1L);
    }
}

答案 1 :(得分:0)

下面的代码工作正常。我在这里所做的是将每个字符的频率存储在数组中,然后将其转换为列表,因为我们稍后需要该点。接下来,我将列表转换为设置并从中删除零,因为与字符相对应的列表中将存在零,而输入字符串中不存在零。如果set在删除零后仅在元素上,则表示所有元素具有相同的频率,因此返回true 。如果set具有两个以上的元素,则意味着无法通过在一个位置删除一个字符来使它成为有效字符串,因此 return false 。现在如果set有两个值,我们从set中获取最小值和最大值。如果有一个字符和一个频率为条件的第一个频率,则可以使其有效。现在第二个条件是,如果b / w max与min的差为1,并且max仅具有一个频率,那么我们可以从max中删除一个字符并使其有效。

static String isValid(String s) {

        Integer arr[] = new Integer[26];
        Arrays.fill(arr, 0);
        //fill the frequency of every character in array arr
        for (int i = 0; i < s.length(); i++) {
            arr[s.charAt(i) - 97]++;
        }
        //convert array to list of integer     
        List<Integer> arrList = Arrays.asList(arr);

        //convert list to set and remove zero bcos zero correspond to char that is not present
        HashSet<Integer> set = new HashSet<Integer>(arrList);
        set.remove(new Integer(0));
        int len = set.size();
        // if size==1 means all freq are same
        if (len == 1)
            return "YES";
        else if (len == 2) {
            List<Integer> list = new ArrayList<>(set);
            int x = list.get(0);
            int y = list.get(1);
            int max = (x > y) ? x : y;
            int min = (x < y) ? x : y;

             // if min elemnnt has value one and freuency one
            if (Collections.frequency(arrList, min) == 1 && min == 1) {
                return "YES";
            }
          //if max-min==1 and there are only one elemnt with value=max      
         else if (max - min == 1) {
                if ((Collections.frequency(arrList, max) == 1)) {
                    return "YES";
                } else {
                    return "NO";
                }
            } 
          // if no of element is more than
          else {
                return "NO";
            }

        } else
            return "NO";
    }

答案 2 :(得分:0)

java 8

import java.util.*;
public class MyClass {
    public static void main(String args[]) {
        Scanner scan = new Scanner(System.in);
        System.out.println(isValid(scan.next()));

    }

    private static String isValid(String s) {
        if (s == null || s.equals("")) {
          return "NO";
        }

        // frequencies ( hashmap) character : frequency 
        // contains frequency of each character of given string input
        Map<Character, Integer> frequencies = new HashMap<>();
        for (char ch : s.toLowerCase().toCharArray())
          frequencies.put(ch, frequencies.getOrDefault(ch, 0) + 1);

............................................... ................................................... ................................................... .....

        // freqTypesCount ( hashmap) 
        // frequency_type : number_of_chars_having this frequency_type 
        Map<Integer, Integer> freqTypesCount = new HashMap<>();
        for (int ch : frequencies.values())
          freqTypesCount.put(ch, freqTypesCount.getOrDefault(ch, 0) + 1);


        if( freqTypesCount.size() == 1){
            // it means all the chars of string occurs same number of time
            return "YES";
        }
        else if( freqTypesCount.size() > 2){
            // aaabbbbccdd
            // a : 3 b: 4 c:2 d:2 --> {3:1, 4:1, 2:2}
            // we can't make this string a valid string just by deleting single char
            return "NO";
        }
        else{
            int valid_freq = Collections.max(freqTypesCount.entrySet(), Map.Entry.comparingByValue()).getKey(); 
            int deleted = 0;

            for (Map.Entry<Character, Integer> entry : frequencies.entrySet())
            { 
                int thisCharCount = entry.getValue();

                if(thisCharCount != valid_freq){

                    if(deleted == 0){
                        if(thisCharCount - 1 == valid_freq || thisCharCount - 1 == 0){
                            deleted += 1;
                        }
                        else{
                            return "NO";
                        }
                    }
                    else{
                         return "NO";
                    }
                }
            }

            return "YES" ;
        }
    }
}

............................................... ................................................... ................................................... ..................

python 3

from collections import Counter

inp_string = input()

def isValidStr( string):
    char_counter_dict = Counter( string)
    count_type_counter_dict = Counter(char_counter_dict.values())

    if len(count_type_counter_dict) == 1:
        return "YES"
    elif len(count_type_counter_dict) > 2:
        return "NO"
    else:
        valid_freq = count_type_counter_dict.most_common()[0][0]

        deleted = 0

        for char,count in char_counter_dict.items():

            if count != valid_freq:

                if deleted == 0:
                    if count - 1 == valid_freq or count - 1 == 0:
                        deleted += 1

                    else:
                        return "NO"

                else:
                    return "NO"


        return "YES"

print(isValidStr(inp_string))