Related
I want to write a method which finds the longest String (word). The output should be the longest word in case of two words with the same lenght the output should be: "More than one longest word".
I used ArrayList and almost had a solution, but something goes wrong. The case is that I have a problem when two words have the same lenght.
The output is :
More than one longest word
More than one longest word
14 incrementation is the longest word
Please check out piece of my code and help me to find the answer :)
public class LongestWord {
public static void main(String[] args) {
ArrayList<String> wordsList = new ArrayList<String>();
wordsList.add("december");
wordsList.add("california");
wordsList.add("cat");
wordsList.add("implementation");
wordsList.add("incrementation");
int largestString = wordsList.get(0).length();
int index = 0;
for (int i = 0; i < wordsList.size(); i++) {
if (wordsList.get(i).length() > largestString) {
largestString = wordsList.get(i).length();
index = i;
}else if(wordsList.get(i).length() == largestString){
largestString = wordsList.get(i).length();
index = i;
System.out.println("More than one longest word");
}
}
System.out.println(largestString +" " + wordsList.get(index) +" is the longest word ");
}
}
The fact is that you can't tell what the biggest word until you have iterated the whole list.
So iterate on the list
if word is bigger than previous largest size : clear list and save word
if word has same size as largest size : save word
if word is smaller : nothing
List<String> wordsList = Arrays.asList(
"december", "california", "cat",
"implementation", "incremntation");
int maxLength = Integer.MIN_VALUE;
List<String> largestStrings = new ArrayList<>();
for (String s : wordsList) {
if (s.length() > maxLength) {
maxLength = s.length();
largestStrings.clear();
largestStrings.add(s);
} else if (s.length() == maxLength) {
largestStrings.add(s);
}
}
if (largestStrings.size() > 1) {
System.out.println("More than one longest word");
System.out.println(largestStrings);
} else {
System.out.println(largestStrings.get(0) + " is the longest word");
}
Gives
More than one longest word
[implementation, incrementation]
azro is right. You can figure out the problem using two iteration. I m not sure but the code below works
for (int i = 0; i < wordsList.size(); i++) {
if (wordsList.get(i).length() > largestString) {
largestString = wordsList.get(i).length();
index = i;
}
}
for (int i = 0; i < wordsList.size(); i++) {
if (wordsList.get(index).length() == wordsList.get(i).length()) {
System.out.println("More than one longest word");
break;
}
}
You can do this with one loop iteration. Storing the longest word(s) as you go.
import java.util.*;
public class Test {
public static void main(String[] args) {
final Collection<String> words = Arrays.asList(
"december", "california", "cat",
"implementation", "incrementation");
final Collection<String> longestWords = findLongestWords(words);
if (longestWords.size() == 1) {
System.out.printf("The longest word is: %s\n", longestWords.iterator().next());
} else if (longestWords.size() > 1) {
System.out.printf("More than one longest word. The longest words are: %s\n", longestWords);
}
}
private static final Collection<String> findLongestWords(final Collection<String> words) {
// using a Set, so that duplicate words are stored only once.
final Set<String> longestWords = new HashSet<>();
// remember the current length of the longest word
int lengthOfLongestWord = Integer.MIN_VALUE;
// iterate over all the words
for (final String word : words) {
// the length of this word is longer than the previously though longest word. clear the list and update the longest length.
if (word.length() > lengthOfLongestWord) {
lengthOfLongestWord = word.length();
longestWords.clear();
}
// the length of this word is currently though to be the longest word, add it to the Set.
if (word.length() == lengthOfLongestWord) {
longestWords.add(word);
}
}
// return an unmodifiable Set containing the longest word(s)
return Collections.unmodifiableSet(longestWords);
}
}
My two cents to make it done in the single loop. Can be improved further.
ArrayList<String> wordsList = new ArrayList<String>();
wordsList.add("december");
wordsList.add("california");
wordsList.add("cat");
wordsList.add("implementation");
wordsList.add("incrementation");
String result;
int length = Integer.MIN_VALUE;
Map<String,String> map = new HashMap<>();
for(String word: wordsList){
if(word.length() >= length) {
length = word.length();
if (map.containsKey(String.valueOf(word.length())) || map.containsKey( "X" + word.length())) {
map.remove(String.valueOf(word.length()));
map.put("X" + word.length(), word);
} else {
map.put(String.valueOf(word.length()), word);
}
}
}
result = map.get(String.valueOf(length)) == null ? "More than one longest word" :
map.get(String.valueOf(length)) + " is the longest word";
System.out.println(result);
Here is one approach. I am using a set to hold the results as there is no reason to include duplicate words if they exist.
iterate over the words
if the current word length is > maxLength, clear the set and add the word, and update maxLength
if equal to the maxLength, just add the word.
List<String> wordsList = List.of("december", "implementation",
"california", "cat", "incrementation");
int maxLength = Integer.MIN_VALUE;
Set<String> results = new HashSet<>();
for (String word : wordsList) {
int len = word.length();
if (len >= maxLength) {
if (len > maxLength) {
results.clear();
maxLength = len;
}
results.add(word);
}
}
System.out.printf("The longest word%s -> %s%n", results.size() > 1 ? "s" : "", results);
prints
The longest words -> [implementation, incrementation]
I changed your code to suggest a different approach to the problem. Honestly, I hope you'll find it fascinating and helpful.
There are two different fashion of it, one that doesn't care about finding more than one longest word (it stamps just the first one - but you can change it as you prefer), and the other one that does.
First solution:
`
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
public class LongestWord {
public static void main(String[] args) {
List<String> wordsList = new ArrayList<>();
wordsList.add("december");
wordsList.add("california");
wordsList.add("cat");
wordsList.add("implementation");
wordsList.add("incrementation");
wordsList.stream()
.max(LongestWord::compare)
.ifPresent(a -> System.out.println(a.toUpperCase() + " is the longest word with length of: " + a.length()));
}
private static int compare(String a1, String b1) {
return a1.length() - b1.length();
}
}
`
Second solution:
`
public class LongestWord {
public static void main(String[] args) {
List<String> wordsList = new ArrayList<>();
wordsList.add("december");
wordsList.add("california");
wordsList.add("cat");
wordsList.add("implementation");
wordsList.add("incrementation");
int max_length = wordsList.stream()
.max(LongestWord::compare)
.map(String::length).orElse(0);
List<String> finalWordsList = wordsList.stream()
.filter(word -> word.length() == max_length)
.collect(Collectors.toList());
if (finalWordsList.size() > 1) {
System.out.println("More than one longest word");
} else {
System.out.println(finalWordsList.get(0) + " is the longest word");
}
}
private static int compare(String a1, String b1) {
return a1.length() - b1.length();
}
}
`
I have solved the problem but was unable to come up with the most efficient problem that passes all test cases. It times out in 5 test cases.
Determine sentences contain all of the words of a phrase
0: chris and jennifer had a fight this morning
1: chris went on a holiday
2: jennifer is in prison
Query Phrases are
0: chris jennifer
1: jennifer
2: prison
Goal is to find indexes of the matching sentences for each query or -1 if there are no matching sentence exists. Order of words does not matter.
Output :
0
0 2
2
i.e.
First query has matching words in sentence 0, second one in sentence 0 and 1. and so on.
Constraints
n: number of sentences
m: number of prases
n, m < 10^4
Number of words in any sentence or query phrase is in range [1-10]
Each word has at most 11 chars
No word appears in more than 10 sentences
Each word consists of upper and lower case alphabet only
Each word must match exactly - i.e. likes and like do not match.
Input Format:
3
chris and jennifer had a fight this morning
chris went on a holiday
jennifer is in prison
3
chris jennifer
jennifer
prison
each 3 represents number of sentences or queries.
The followings were what I tried...
1. My first solution :
Make HashMap per each sentence
For each splitted word in phrase :
2-1. check if all words exists in the sentence hashmap
2-2. If so store the index
2-3. If there is no matching sentences exist for all sentences, store -1.
Print result
let p = the largest number of words in a sentence
let k = the largest number of words in a query
Big O is O(npk)
public static void textQueries(List<String> sentences, List<String> queries) {
List<Map<String, Integer>> sentenceMaps = createMaps(sentences);
String results = queryMatcher(sentenceMaps, queries);
System.out.println(results);
}
private static String queryMatcher(List<Map<String, Integer>> sentenceMaps, List<String> queries) {
Map<String, Integer> wordCounter = new LinkedHashMap<>();
List<List<String>> results = new ArrayList<List<String>>();
for (String query : queries) {
List<String> result = new ArrayList<>();
for (int j = 0; j < sentenceMaps.size(); j++) {
if (isQueryFound(sentenceMaps.get(j), query, wordCounter)) {
result.add(j + "");
}
}
results.add(result);
}
return generateResultString(results);
}
/*
* StringBuilder used to reduce delays of calling multiple System.out.println();
*/
private static String generateResultString(List<List<String>> results) {
StringBuilder stringBuilder = new StringBuilder();
for (List<String> matchingSentenceIndexes : results) {
if (matchingSentenceIndexes.isEmpty()) {
stringBuilder.append("-1\n");
} else {
resultStringHelper(matchingSentenceIndexes, stringBuilder);
}
//stringBuilder.append("\n");
}
return stringBuilder.toString();
}
/*
* add " " for multiple indexes result
*/
private static void resultStringHelper(List<String> result, StringBuilder stringBuilder) {
for (int i = 0; i < result.size(); i++) {
stringBuilder.append(result.get(i));
if (i < result.size() - 1) {
stringBuilder.append(" ");
} else if (i == result.size() - 1) {
stringBuilder.append("\n");
}
}
}
private static boolean isQueryFound(Map<String, Integer> sentenceMap, String query, Map<String, Integer> wordCounter) {
String[] queryTokens = query.split(" ");
for (String queryToken : queryTokens) {
if (isMoreThan10Sentences(wordCounter, queryToken)) return false;
if (sentenceMap.containsKey(queryToken)) {
wordCounter.put(queryToken, wordCounter.getOrDefault(queryToken, 0) + 1);
} else {
return false;
}
}
return true;
}
private static boolean isMoreThan10Sentences(Map<String, Integer> wordCounter, String token) {
return wordCounter.getOrDefault(token, -1) > 10;
}
private static Map<String, Integer> initMap(String[] tokens) {
Map<String, Integer> map = new LinkedHashMap<>();
for (String token : tokens) {
map.put(token, 0);
}
return map;
}
private static List<Map<String, Integer>> createMaps(List<String> sentences) {
List<Map<String, Integer>> maps = new ArrayList<Map<String,Integer>>();
for (int i = 0; i < sentences.size(); i++) {
String[] tokens = sentences.get(i).split(" ");
maps.add(initMap(tokens));
}
return maps;
}
Timeout in the last 5 test cases.
For small test cases, the benchmark is the following on their online coding server:
Map creation time: 9.23954E-4
Query matching time: 3.85751E-4
Map generation is expensive.
2. My second try:
Similar logic but applied concurrency, as the platform supports up to 2 threads.
Multi-threading is done here :
1. Sentence -> Map generation (Concurrent map generation)
2. Query matching (Concurrent matching)
public static void textQueries(List<String> sentences, List<String> queries) {
List<Map<String, Integer>> sentenceMaps = createMaps(sentences);
startTime = System.nanoTime();
String results = queryMatcher(sentenceMaps, queries);
System.out.println(results);
private static String queryMatcher(List<Map<String, Integer>> sentenceMaps, List<String> queries) {
List<Future<String>> futures = new ArrayList<Future<String>>();
int threads = Runtime.getRuntime().availableProcessors();
ExecutorService executor = Executors.newFixedThreadPool(threads);
String[] results = new String[threads];
int length = queries.size() / threads;
for (int i = 0; i < threads; i++) {
int queryStart = length * i;
int queryEnd = length * (i+1);
if (i == threads -1 && queries.size() % threads != 0) queryEnd++;
Callable<String> worker = new QueryMatcher(sentenceMaps, queries, queryStart, queryEnd);
Future<String> submit = executor.submit(worker);
futures.add(submit);
}
for (int i = 0; i < futures.size(); i++) {
try {
results[i] = futures.get(i).get();
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
}
String returnString = concaString(results);
executor.shutdown();
return returnString;
}
private static String concaString(String[] results) {
StringBuilder stringBuilder = new StringBuilder();
for (int i = 0; i < results.length; i++) {
stringBuilder.append(results[i]);
}
return stringBuilder.toString();
}
private static String generateResultString(List<List<String>> results) {
StringBuilder stringBuilder = new StringBuilder();
for (List<String> matchingSentenceIndexes : results) {
if (matchingSentenceIndexes.isEmpty()) {
stringBuilder.append("-1\n");
} else {
resultStringHelper(matchingSentenceIndexes, stringBuilder);
}
//stringBuilder.append("\n");
}
return stringBuilder.toString();
}
private static void resultStringHelper(List<String> result, StringBuilder stringBuilder) {
for (int i = 0; i < result.size(); i++) {
stringBuilder.append(result.get(i));
if (i < result.size() - 1) {
stringBuilder.append(" ");
} else if (i == result.size() - 1) {
stringBuilder.append("\n");
}
}
}
private static boolean isQueryFound(Map<String, Integer> sentenceMap, String query, Map<String, Integer> wordCounter) {
String[] queryTokens = query.split(" ");
for (String queryToken : queryTokens) {
if (isMoreThan10Sentences(wordCounter, queryToken)) return false;
if (sentenceMap.containsKey(queryToken)) {
wordCounter.put(queryToken, wordCounter.getOrDefault(queryToken, 0) + 1);
} else {
return false;
}
}
return true;
}
private static boolean isMoreThan10Sentences(Map<String, Integer> wordCounter, String token) {
return wordCounter.getOrDefault(token, -1) > 10;
}
private static boolean isQueryFound(Map<String, Integer> sentenceMap, String query) {
String[] queryTokens = query.split(" ");
//Map<String, Integer> duplicateChecker = new LinkedHashMap<String, Integer>();
for (String queryToken : queryTokens) {
if (sentenceMap.containsKey(queryToken)) {
//if (!duplicateChecker(duplicateChecker, sentenceMap, queryToken))
//return false;
} else {
return false;
}
}
return true;
}
/*
* this method checks for the case when there are duplicate words in query
* i.e. sentence containing 2 hello will return false of queries with 3 hello
*/
private static boolean duplicateChecker(Map<String, Integer> duplicateChecker, Map<String, Integer> sentenceMap, String queryToken) {
if (duplicateChecker.containsKey(queryToken)) {
if (duplicateChecker.get(queryToken) == 0) return false;
duplicateChecker.put(queryToken, duplicateChecker.get(queryToken) - 1);
} else {
duplicateChecker.put(queryToken, sentenceMap.get(queryToken) - 1);
}
return true;
}
private static List<Map<String, Integer>> createMaps(List<String> sentences) {
List<Map<String, Integer>> maps = new ArrayList<>();
int threads = Runtime.getRuntime().availableProcessors();
ExecutorService executor = Executors.newFixedThreadPool(threads);
List<Future<List<Map<String, Integer>>>> futures = new ArrayList<Future<List<Map<String, Integer>>>>();
int length = (sentences.size()) / threads;
for (int i = 0; i < threads; i++) {
int start = i * length;
int end = (i+1) * length;
if (i == threads - 1 && sentences.size() % threads != 0) end++;
List<String> splitSentence = new ArrayList(sentences.subList(start, end));
Callable<List<Map<String, Integer>>> worker = new MapMaker(splitSentence);
Future<List<Map<String, Integer>>> submit = executor.submit(worker);
futures.add(submit);
}
for (int i = 0; i < futures.size(); i++) {
try {
for (Map<String, Integer> map : futures.get(i).get()) {
maps.add(map);
}
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
}
executor.shutdown();
return maps;
}
private synchronized static Map<String, Integer> initMap(String[] tokens) {
Map<String, Integer> map = new LinkedHashMap<>();
for (String token : tokens) {
map.put(token, 0);
// map.put(token, map.getOrDefault(map.get(token), 1) + 1);
}
return map;
}
public static class MapMaker implements Callable<List<Map<String, Integer>>> {
private List<String> sentences;
#Override
public List<Map<String, Integer>> call() throws Exception {
List<Map<String, Integer>> maps = new ArrayList<Map<String,Integer>>();
for (int i = 0; i < sentences.size(); i++) {
String[] tokens = sentences.get(i).split(" ");
maps.add(initMap(tokens));
}
return maps;
}
public MapMaker(List<String> sentences) {
this.sentences = sentences;
}
}
public static class QueryMatcher implements Callable<String> {
private List<Map<String, Integer>> sentenceMaps;
private List<String> queries;
private int queryStart;
private int queryEnd;
#Override
public String call() throws Exception {
List<List<String>> results = new ArrayList<List<String>>();
for (int i = queryStart; i < queryEnd; i++) {
List<String> result = new ArrayList<>();
String query = queries.get(i);
for (int j = 0; j < sentenceMaps.size(); j++) {
if (isQueryFound(sentenceMaps.get(j), query)) {
result.add(j + "");
}
}
results.add(result);
}
return generateResultString(results);
}
public QueryMatcher(List<Map<String, Integer>> sentenceMaps, List<String> queries, int queryStart, int queryEnd) {
this.sentenceMaps = sentenceMaps;
this.queries = queries;
this.queryStart = queryStart;
this.queryEnd = queryEnd;
}
}
Although I hoped for some speedup for large test case, it still gave 5 test cases timeout.
And for small test cases, it increased map generation time due to additional overhead on creating pools.
Benchmark time:
Map time: 0.007669489
Query matching time: 3.22923E-4
3. My third solution - Coding the above in C++
I questioned whether it could be Java that gives the timeout.
The platform actually gives shorter computation time for C++, so to my suprise, it still gave same 5 timeouts.
4. My 4th approach Regex,
I knew it would be slower, but I still did in futile attempt.
The Big O is actually slower here, as I need to sort each sentences by words to avoid n! permutation of regex...
public static void textQueries(List<String> sentences, List<String> queries) {
stringSort(sentences);
stringSort(queries);
StringBuilder stringBuilder = new StringBuilder();
boolean isExist = false;
for (int index = 0; index < queries.size(); index++) {
String query = queries.get(index);
isExist = false;
for (int i = 0; i < sentences.size(); i++) {
if (Matcher(buildNaturalLanguage(query), sentences.get(i))) {
stringBuilder.append(i + " ");
isExist = true;
}
}
if (!isExist) stringBuilder.append("-1");
if (index != queries.size() - 1) stringBuilder.append("\n");
}
System.out.println(stringBuilder.toString());
}
private static void stringSort(List<String> strings) {
for (int i = 0; i < strings.size(); ++i) {
String string = strings.get(i);
String[] stringParts = string.split(" ");
StringBuilder stringBuilder = new StringBuilder();
Arrays.sort(stringParts);
for (int j = 0; j < stringParts.length; j++) {
stringBuilder.append(stringParts[j] + " ");
}
strings.set(i, stringBuilder.toString()); // sure I made it back to string for code cleaness but you can return String[] for efficiency.. But only minor improvement.
}
}
private static String buildNaturalLanguage(String query) {
// System.out.println("query " + query);
String[] stringParts = query.split(" ");
String regular = "(([a-zA-Z])*(\\s))*";
for (String word : stringParts) {
regular += word + "(\\s(([a-zA-Z])*(\\s))*)";
}
return regular;
}
private static boolean Matcher(String regular, String sentence) {
Pattern p = Pattern.compile(regular);
Matcher m = p.matcher(sentence);
return m.find();
}
Result :
Not only getting timeout, it is somehow causing error (wrong answer) on 2 additional undisclosed test cases.. I have no idea why..
Ω(nm^2 + plogp).. assuming regex matching is O(m)
I can only think of possibility of filtering some query or sentences before even runnning the main algorithm? (constraint : 10 max matching per word).
This constraint checking part is still implemented with my first and second solution however. So smarter filtering might be required.
The thing is I think the BCR - best conceivable rate is O(MNP), you would still need to go through each query and sentences, and also split them if not using regex.
I am totally lost here, how can I actually increase the speed further than this?
Many thanks in advance.
Maintain a HashMap that will map Strings to Set<Int>. The idea is to keep track of what sentences a given word appears in. We use a set instead of an array in order to support computing the intersection of two sets efficiently.
For each input sentence:
Tokenize it into words, and add the index of the current sentence to the Set corresponding to the current token.
For each query phrase:
Tokenize it into words.
Query for the Set of indices corresponding to each word
Take the intersection of all of these sets.
Time Complexity: Given that there are 10 words in each sentence, the cost of building the HashMap is O(10N log N). The cost of each query is O(10 * log(N)).
I have following idea that may speed up , it seems similar to what Rishav proposed:
public static void main(String[] args) throws FileNotFoundException {
Scanner sc = new Scanner(new FileInputStream("file.txt"));
int numberOfSentences = Integer.parseInt(sc.nextLine());
Set<Integer> sentences = new HashSet<Integer>();
Map<String, Set<Integer>> words2Sentences = new HashMap<String, Set<Integer>>();
for (int i = 0; i < numberOfSentences; i++) {
String words[] = sc.nextLine().split(" ");
for (int j = 0; j < words.length; j++) {
if (!words2Sentences.containsKey(words[j])) {
words2Sentences.put(words[j], new HashSet<Integer>());
}
words2Sentences.get(words[j]).add(i);
}
sentences.add(i);
}
int numberOfPhrases = Integer.parseInt(sc.nextLine());
List<Set<Integer>> phraseResults = new ArrayList<Set<Integer>>();
for (int i = 0; i < numberOfPhrases; i++) {
Set<String> phrases = new HashSet<String>(Arrays.asList(sc.nextLine().split(" ")));
Set<Integer> result = new HashSet(sentences);
for (String s : phrases) {
result.retainAll(words2Sentences.get(s));
}
phraseResults.add(result);
}
for (Set<Integer> set : phraseResults) {
for (Integer i : set) {
System.out.print(i);
}
System.out.println();
}
}
private static void printAllQeriesIndeicesInSentence(List<String> sentences,
List<String> queries) {
Map<Integer, List<String>> sentenceMap = new HashMap<>();
for(int i=0; i < sentences.size(); i++){
List<String> words = Arrays.asList(sentences.get(i).split("\\s+"));
sentenceMap.put(i, words);
}
for(String query: queries){
List<String> queryList = Arrays.asList(query.split("\\s+"));
for(Map.Entry<Integer, List<String>> e: sentenceMap.entrySet()){
List<String> wordsList = e.getValue();
if(wordsList.containsAll(queryList)){
System.out.print(e.getKey() + " ");
}
}
System.out.println();
}
}
This approach should work.
#include <bits/stdc++.h>
using namespace std;
vector<set<int>> getres(vector<string> sentences, vector<string> phrases, vector<set<int>> v){
map<string,set<int>> m;
map<string,set<int>> :: iterator itr;
for(int i=0;i<sentences.size();i++){
string temp = sentences[i];
temp.push_back(' ');
string word = "";
for(int j=0;j<temp.length();j++){
if(temp[j] == ' '){
itr = m.find(word);
if(itr == m.end()){
set<int> s;
s.insert(i);
m.insert({word,s});
}
else if(itr != m.end()){
itr->second.insert(i);
}
word = "";
}
else{
word.push_back(temp[j]);
}
}
}
// for(itr = m.begin();itr!= m.end();itr++){
// cout<<itr->first <<" ";
// for(auto f= itr->second.begin();f!= itr->second.end();f++){
// cout<<*f<<" ";
// }
// cout<<endl;
// }
for(int i=0;i<phrases.size();i++){
string temp = phrases[i];
temp.push_back(' ');
string word = "";
int flag = 0;
set<int> s1,s2,s3;
for(int j=0;j<temp.length();j++){
if(temp[j] == ' '){
// cout<<"yes";
itr = m.find(word);
if(itr == m.end()){
flag = 1;
break;
}
else if(itr != m.end()){
if(s1.empty()){
s1 = itr->second;
}
else{
set_intersection(s1.begin(),s1.end(),itr->second.begin(),itr->second.end(),inserter(s3,s3.begin()));
s1 = s3;
s3.clear();
if(s1.empty()){
flag = 1;
break;
}
}
// for(auto f=s1.begin();f!= s1.end();f++){
// cout<<*f<<" ";
// }
// cout<<endl;
}
word = "";
}
else{
word.push_back(temp[j]);
}
}
if(flag == 1){
s1.clear();
s1.insert(-1);
v[i] = s1;
flag = 0 ;
}
else{
v[i] = s1;
}
s1.clear();
s2.clear();
s3.clear();
}
return v;
}
int main() {
vector<string> sentences = {"chris and jennifer had a fight this morning", "chris went on a holiday", "jennifer is in prison"};
vector<string> phrases = {"chris jennifer", "jennifer", "prison"};
vector<set<int>> v(phrases.size());
v = getres(sentences,phrases,v);
for(int i=0;i<v.size();i++){
set<int> :: iterator itr;
for(itr = v[i].begin() ;itr != v[i].end();itr++){
cout<<*itr<<" ";
}
cout<<endl;
}
// cout<<"finish"<<endl;
}
For instance suppose I have the following String
String S = "5,a\n" +
"6,b\n" +
"9,a";
The format is always the same - one digit, then comma, then one character and then line end character.
For looping each row in String I use
for(String a : S.split("\\n")){}
I want to learn the character with highest amount, when grouped by character. For Instance, there is only one "b", so value is 6; whereas "a" has two lines, so its value is 5 + 9 = 14. Since 14 is maximum here, I want to find out "a" and 14 and save them in variables.
You can do something like below :
public static void main (String[] args) throws java.lang.Exception
{
String S = "5,a\n" +
"6,b\n" +
"9,a";
String[] lines = S.split("\\n");
Map<String, Integer> map = new HashMap<String, Integer>();
for( String t : lines )
{
String[] e = t.split(",");
Integer digit = Integer.parseInt(e[0]);
String c = e[1];
if ( map.get(c) != null )
{
Integer val = map.get(c);
val += digit;
map.put( c, val );
}
else
{
map.put( c, digit );
}
}
int max = 0;
String maxKey = null;
for ( String k : map.keySet() )
{
if ( map.get(k) > max )
{
max = map.get(k);
maxKey = k;
}
}
System.out.println("The maximum key is : " + maxKey );
System.out.println("The maximum value is : " + max );
}
Output is :
The maximum key is : a
The maximum value is : 14
Use a HashMap to store each pair, with the letter as the key. If the entry doesn't exist, put the first number. If it exists, get the entry and add the number, and then put the sum.
import java.util.HashMap;
import java.util.Map;
public class ParseTest {
public static void main(String[] args) {
String S = "5,a\n" + "6,b\n" + "9,a";
String maxKey = null;
int maxVal = 0;
Map<String, Integer> sums = new HashMap<>();
for (String a : S.split("\\n")) {
String[] split = a.split(",");
int value = Integer.parseInt(split[0]);
String key = split[1];
if (sums.containsKey(key)) {
sums.put(key, sums.get(key) + value);
} else {
sums.put(key, value);
}
if (sums.get(key) > maxVal) {
maxVal = sums.get(key);
maxKey = key;
}
}
System.out.println("Max key: " + maxKey + ", Sum: " + maxVal);
}
}
After finishing my answer, I found that many similar answers have been posted out :). Anyway, my solution:
public static void main(String[] args) {
String S = "5,a\n6,b\n9,a";
Map<String, Integer> map = new HashMap<String, Integer>();
String highestAmountChar = "";
int highestAmount = 0;
for (String str : S.split("\\n")) {
String[] amountChar = str.split(",");
if (map.get(amountChar[1]) == null) {
map.put(amountChar[1], Integer.parseInt(amountChar[0]));
} else {
map.put(amountChar[1], map.get(amountChar[1]) + Integer.parseInt(amountChar[0]));
}
if (highestAmount < map.get(amountChar[1])) {
highestAmount = map.get(amountChar[1]);
highestAmountChar = amountChar[1];
}
}
System.out.println("The character " + highestAmountChar + " has highest amount " + highestAmount);
}
You could use something like this without using HashMap or any collection for that matter
import java.util.Arrays;
public class Test {
public static void main(String args[]) {
String S = "5,a\n" +
"6,b\n" +
"9,a";
// Separate the string by number and letter
String[] separated = S.split("\\n");
// Create a new array to store the letters only
char[] letters = new char[separated.length];
// Write the letter
for (int i = 0; i < letters.length; i++) {
letters[i] = separated[i].charAt(2);
}
// Sort them haha
Arrays.sort(letters);
// And now find out which letter is repeated most
// Store the first letter
char previous = letters[0];
// Make it the most repeated one for now
char mostRepeated = letters[0];
int count = 1;
int maxCount = 1;
for (int i = 1; i < letters.length; i++) {
// since the array is sorted if the actual letter is the same as the previous one then keep counting
if (letters[i] == previous)
count++;
else {
if (count > maxCount) {
mostRepeated = letters[i - 1];
maxCount = count;
}
previous = letters[i];
count = 1;
}
}
char answer = count > maxCount ? letters[letters.length-1] : mostRepeated;
// Once you get the letter now just add all the numbers that goes with it
int sum = 0;
for (String s:separated) {
if (s.charAt(2) == answer) {
sum += Character.getNumericValue(s.charAt(0));
}
}
// Print the result by printing the letter and it sum
}
}
My code is throwing an error that I've never seen before. So hey! I guess I'm learning ;) Anyway, I did some reading and generally this error is thrown when a list that is being iterated over is modified mid-iteration. However, I'm pretty sure I'm not modifying it. While the error is being thrown on partition(), if I don't assign a new value for currentList in updateCurrentList() (by commenting out the code), the program no longer throws the error. These two functions are called one after the other in my play() method, however the list iteration should be complete by the time the change is made. What am I missing? Do I have to close down the iterator somehow?
package hangman;
import java.io.*;
import java.util.*;
import javax.swing.JOptionPane;
public class Hangman {
private Map<String, List<String>> wordPartitions; // groups words according to positions of guessed letter
private List<String> currentList; // remaining possible words that fit the information given so far
Set<Character> wrongGuesses; // holds all the "wrong" guesses so far
StringBuilder guessString; // current state of the word being guessed
String justHyphens; // for checking whether a guess was "wrong"
// initialize fields
// currentList should contain all (and only) words of length wordLength
// justHyphens and guessString should consist of wordLength hyphens
public Hangman(int wordLength) throws FileNotFoundException {
this.currentList = new ArrayList<String>();
addWords(wordLength);
wrongGuesses = new HashSet();
for(int i = 0; i < wordLength; i++) {
justHyphens += "-";
}
guessString = new StringBuilder();
wordPartitions = new HashMap();
}
private void addWords(int wordLength) throws FileNotFoundException {
Scanner words = new Scanner(new File("lexicon.txt"));
String word = "";
while(words.hasNext()) {
word = words.next();
if (word.length() == wordLength) {
currentList.add(word);
}
}
}
// main loop
public void play() {
char choice;
do {
choice = getUserChoice();
partition(choice);
updateCurrentList(choice);
} while (!gameOver());
endMessage();
}
// display the guessString and the missed guesses
// and get the next guess
private char getUserChoice() {
//generate a string from the incorrect choices char list
String wrong = "";
char letter;
if(!wrongGuesses.isEmpty()) {
Iterator<Character> letters = wrongGuesses.iterator();
letter = letters.next();
while(letters.hasNext()) {
letter = letters.next();
wrong += ", " + letter;
}
}
String letterStr = JOptionPane.showInputDialog("Incorrect choices: "+ wrong +"\n Tested letters: "+ guessString.toString() +"\nplease input a letter.");
return letterStr.charAt(0);
}
// use wordPartitions to partition currentList using
// keys returned by getPartitionKey()
private void partition(char choice) {
String word = "";
String key = "";
List<String> tempList = new ArrayList<String>();
Iterator<String> words = currentList.iterator();
//Generate a key for each word and add to appropriate arraylist within map.
while(words.hasNext()) {
word = words.next();
key = getPartitionKey(word, choice);
if(wordPartitions.containsKey(key)) {
tempList = wordPartitions.get(key);
tempList.add(word);
wordPartitions.put(key, tempList);
} else {
tempList.clear();
tempList.add(word);
wordPartitions.put(key, new ArrayList<String>());
}
}
}
// update currentList to be a copy of the longest partition
// if choice was "wrong", add choice to wrongGuesses
// if choice was "right", update guessString
private void updateCurrentList(char choice) {
String key = findLongestList();
currentList = wordPartitions.get(key);
if(key.equals(justHyphens)) {
wrongGuesses.add(choice);
} else {
addLetterToGuessString(guessString, choice, key);
}
}
private String findLongestList() {
Set<String> keySet = wordPartitions.keySet();
Iterator<String> keys = keySet.iterator();
String maxKey = "";
int maxKeyLength = 0;
List<String> tempList;
String tempKey = "";
while(keys.hasNext()) {
tempKey = keys.next();
tempList = wordPartitions.get(tempKey);
if(tempList.size() > maxKeyLength) {
maxKeyLength = tempList.size();
maxKey = tempKey;
}
}
return maxKey;
}
// checks for end of game
private boolean gameOver() {
return false;
}
// display the guessString and the missed guesses
// and print "Congratulations!"
private void endMessage() {
JOptionPane.showMessageDialog(null, "Congrats, yo!");
}
// returns string with '-' in place of each
// letter that is NOT the guessed letter
private String getPartitionKey(String s, char c) {
String word = "";
String letter = Character.toString(c);
for(int i = 0; i < s.length(); i++) {
if(s.charAt(i) == c) {
word += letter;
} else {
word += "-";
}
}
return word;
}
// update guessString with the guessed letter
private void addLetterToGuessString(StringBuilder guessString, char letter, String key) {
for(int i = 0; i < key.length(); i++) {
if(key.charAt(i) != '-') {
guessString.setCharAt(i, key.charAt(i));
}
}
}
}
The problem is that you are modifying a collection while you are iterating over it.
The collection is currentList, you are iterating over it in partition(). You modify it when you add a word to tempList here:
key = getPartitionKey(word, choice);
if(wordPartitions.containsKey(key)) {
tempList = wordPartitions.get(key);
tempList.add(word);
wordPartitions.put(key, tempList);
} else {
Why ? Because previously you called updateCurrentList() from play():
do {
choice = getUserChoice();
partition(choice);
updateCurrentList(choice);
} while (!gameOver());
And you updated currentList:
String key = findLongestList();
currentList = wordPartitions.get(key);
So, if the key returned by getPartitionKey(word, choice) is the same as the key previously returned by findLongestList(), currentListwill be the same as tempList, and so you will be modifying the collection you are iterating over.
The solution ? If tempList is the same as currentList, don't add the word to it (it already have the word, by definition). So, you can rewrite your if-else like that (I removed some useless code):
if(wordPartitions.containsKey(key)) {
tempList = wordPartitions.get(key);
} else {
wordPartitions.put(key, new ArrayList<String>());
}
if (tempList!=currentList) {
tempList.add(word);
}
This is my class Debugger. Can anyone try and run it and see whens wrong? Ive spent hours on it already. :(
public class Debugger {
private String codeToDebug = "";
public Debugger(String code) {
codeToDebug = code;
}
/**
* This method itterates over a css file and adds all the properties to an arraylist
*/
public void searchDuplicates() {
boolean isInside = false;
ArrayList<String> methodStorage = new ArrayList();
int stored = 0;
String[] codeArray = codeToDebug.split("");
try {
int i = 0;
while(i<codeArray.length) {
if(codeArray[i].equals("}")) {
isInside = false;
}
if(isInside && !codeArray[i].equals(" ")) {
boolean methodFound = false;
String method = "";
int c = i;
while(!methodFound) {
method += codeArray[c];
if(codeArray[c+1].equals(":")) {
methodFound = true;
} else {
c++;
}
}
methodStorage.add(stored, method);
System.out.println(methodStorage.get(stored));
stored++;
boolean stillInside = true;
int skip = i;
while(stillInside) {
if(codeArray[skip].equals(";")) {
stillInside = false;
} else {
skip++;
}
}
i = skip;
}
if(codeArray[i].equals("{")) {
isInside = true;
}
i++;
}
} catch(ArrayIndexOutOfBoundsException ar) {
System.out.println("------- array out of bounds exception -------");
}
}
/**
* Takes in String and outputs the number of characters it contains
* #param input
* #return Number of characters
*/
public static int countString(String input) {
String[] words = input.split("");
int counter = -1;
for(int i = 0; i<words.length; i++){
counter++;
}
return counter;
}
public static void main(String[] args) {
Debugger h = new Debugger("body {margin:;\n}");
h.searchDuplicates();
}
}
Any place where an element of an array is being obtained without a bounds check after the index is manipulated is an candidate for an ArrayIndexOutOfBoundsException.
In the above code, there are at least two instances where the index is being manipulated without being subject to a bounds check.
The while loop checking the !methodFound condition
The while loop checking the stillInside condition
In those two cases, the index is being manipulated by incrementing or adding a value to the index, but there are no bound checks before an element is being obtained from the String[], therefore there is no guarantee that the index being specified is not outside the bounds of the array.
I think this block of codes can create your problem
int c = i;
while(!methodFound) {
method += codeArray[c];
if(codeArray[c+1].equals(":")) {
methodFound = true;
} else {
c++;
}
}
int skip = i;
while(stillInside) {
if(codeArray[skip].equals(";")) {
stillInside = false;
} else {
skip++;
}
}
i = skip;
The reason is that if the condition is true, and i = codeArray.length - 1. The c + 1 will create the error of ArrayIndexOutOfBound
Try evaluating if your index exists in the array...
adding:
while (!methodFound && c < codeArray.length) {
while (stillInside && skip < codeArray.length) {
if (i < codeArray.length && codeArray[i].equals("{")) {
so, your code looks like:
public class Debugger {
private String codeToDebug = "";
public Debugger(String code) {
codeToDebug = code;
}
/**
* This method itterates over a css file and adds all the properties to an
* arraylist
*/
public void searchDuplicates() {
boolean isInside = false;
List<String> methodStorage = new ArrayList<String>();
int stored = 0;
String[] codeArray = codeToDebug.split("");
try {
int i = 0;
while (i < codeArray.length) {
if (codeArray[i].equals("}")) {
isInside = false;
}
if (isInside && !codeArray[i].equals(" ")) {
boolean methodFound = false;
String method = "";
int c = i;
while (!methodFound && c < codeArray.length) {
method += codeArray[c];
if (codeArray[c].equals(":")) {
methodFound = true;
} else {
c++;
}
}
methodStorage.add(stored, method);
System.out.println(methodStorage.get(stored));
stored++;
boolean stillInside = true;
int skip = i;
while (stillInside && skip < codeArray.length) {
if (codeArray[skip].equals(";")) {
stillInside = false;
} else {
skip++;
}
}
i = skip;
}
if (i < codeArray.length && codeArray[i].equals("{")) {
isInside = true;
}
i++;
}
} catch (ArrayIndexOutOfBoundsException ar) {
System.out.println("------- array out of bounds exception -------");
ar.printStackTrace();
}
}
/**
* Takes in String and outputs the number of characters it contains
*
* #param input
* #return Number of characters
*/
public static int countString(String input) {
String[] words = input.split("");
int counter = -1;
for (int i = 0; i < words.length; i++) {
counter++;
}
return counter;
}
public static void main(String[] args) {
Debugger h = new Debugger("body {margin:prueba;\n}");
h.searchDuplicates();
}
}
Also, declaring implementation types is a bad practice, because of that in the above code i Change the ArrayList variable = new ArrayList() to List variable = new ArrayList()
I couldn't resist to implement this task of writing a CSS parser in a completely different way. I have split the task of parsing into many small ones.
The smallest is called skipWhitespace, since you will need it everywhere when parsing text files.
The next one is parseProperty, which reads one property of the form name:value;.
Based on that, parseSelector reads a complete CSS selector, starting with the selector name, an opening brace, possibly many properties, and finishing with the closing brace.
Still based on that, parseFile reads a complete file, consisting of possibly many selectors.
Note how carefully I checked whether the index is small enough. I did that before every access to the chars array.
I used LinkedHashMaps to save the properties and the selectors, because these kinds of maps remember in which order the things have been inserted. Normal HashMaps don't do that.
The task of parsing a text file is generally quite complex, and this program only attempts to handle the basics of CSS. If you need a full CSS parser, you should definitely look for a ready-made one. This one cannot handle #media or similar things where you have nested blocks. But it shouldn't bee too difficult to add it to the existing code.
This parser will not handle CSS comments very well. It only expects them at a few places. If comments appear in other places, the parser will not treat them as comments.
import java.util.LinkedHashMap;
import java.util.Map;
public class CssParser {
private final char[] chars;
private int index;
public Debugger(String code) {
this.chars = code.toCharArray();
this.index = 0;
}
private void skipWhitespace() {
/*
* Here you should also skip comments in the CSS file, which either look
* like this comment or start with a // and go until the end of line.
*/
while (index < chars.length && Character.isWhitespace(chars[index]))
index++;
}
private void parseProperty(String selector, Map<String, String> properties) {
skipWhitespace();
// get the CSS property name
StringBuilder sb = new StringBuilder();
while (index < chars.length && chars[index] != ':')
sb.append(chars[index++]);
String propertyName = sb.toString().trim();
if (index == chars.length)
throw new IllegalArgumentException("Expected a colon at index " + index + ".");
// skip the colon
index++;
// get the CSS property value
sb.setLength(0);
while (index < chars.length && chars[index] != ';' && chars[index] != '}')
sb.append(chars[index++]);
String propertyValue = sb.toString().trim();
/*
* Here is the check for duplicate property definitions. The method
* Map.put(Object, Object) always returns the value that had been stored
* under the given name before.
*/
String previousValue = properties.put(propertyName, propertyValue);
if (previousValue != null)
throw new IllegalArgumentException("Duplicate property \"" + propertyName + "\" in selector \"" + selector + "\".");
if (index < chars.length && chars[index] == ';')
index++;
skipWhitespace();
}
private void parseSelector(Map<String, Map<String, String>> selectors) {
skipWhitespace();
// get the CSS selector
StringBuilder sb = new StringBuilder();
while (index < chars.length && chars[index] != '{')
sb.append(chars[index++]);
String selector = sb.toString().trim();
if (index == chars.length)
throw new IllegalArgumentException("CSS Selector name \"" + selector + "\" without content.");
// skip the opening brace
index++;
skipWhitespace();
Map<String, String> properties = new LinkedHashMap<String, String>();
selectors.put(selector, properties);
while (index < chars.length && chars[index] != '}') {
parseProperty(selector, properties);
skipWhitespace();
}
// skip the closing brace
index++;
}
private Map<String, Map<String, String>> parseFile() {
Map<String, Map<String, String>> selectors = new LinkedHashMap<String, Map<String, String>>();
while (index < chars.length) {
parseSelector(selectors);
skipWhitespace();
}
return selectors;
}
public static void main(String[] args) {
CssParser parser = new CssParser("body {margin:prueba;A:B;a:Arial, Courier New, \"monospace\";\n}");
Map<String, Map<String, String>> selectors = parser.parseFile();
System.out.println("There are " + selectors.size() + " selectors.");
for (Map.Entry<String, Map<String, String>> entry : selectors.entrySet()) {
String selector = entry.getKey();
Map<String, String> properties = entry.getValue();
System.out.println("Selector " + selector + ":");
for (Map.Entry<String, String> property : properties.entrySet()) {
String name = property.getKey();
String value = property.getValue();
System.out.println(" Property name \"" + name + "\" value \"" + value + "\"");
}
}
}
}