The task is to write the logic of checking the magnitude of the coincidence of the player's attempt with the hidden word. More formally, let there be a string S — a hidden word and a string Q — a player's attempt. Both strings have the same length N. For each position 1 ≤ i ≤ N of string Q, we need to calculate the type of match in this position with string S.
If Q[i] = S[i], then at position i the match type should be equal to "correct".
If Q[i]≠S[i], but there is another position 1 ≤ j≤ N such that Q[i] = S[j], then in position i the match type must be equal to "present".
- Each letter of the string S can be used in no more than one match of the type "correct" or "present".
- Priority is always given to the "correct" type.
- Of all possible use cases in the "present" type, the program selects the leftmost position in the Q string.
In other positions, the match type must be equal to "absent".
Input format:
The first line contains the string S (1≤ S ≤ 10^6) — the hidden word. The second line contains the string Q ( Q = S) — the player's attempt. It is guaranteed that the strings S and Q contain only uppercase Latin letters.
Example:
input: COVER CLEAR
output: correct absent present absent correct
My program does this very slowly, how can I speed it up?
import java.util.*;
public class Task1 {
private final static String cor = "correct";
private final static String abs = "absent";
private final static String pre = "present";
static String[] stringArr;
static java.util.Map<Integer, Integer> map = new HashMap<>();
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
String a = sc.nextLine();
String b = sc.nextLine();
stringArr = new String[a.length()];
int length1 = a.length();
char[] arr1 = a.toCharArray();
char[] arr2 = b.toCharArray();
for (int i = 0; i < length1; i ) {
if (arr2[i] == arr1[i]) {
stringArr[i] = cor;
map.put(i, i);
}
}
for (int i = 0; i < length1; i ) {
if (arr2[i] != arr1[i]) {
while (stringArr[i] == null) {
boolean finded = false;
for (int j = 0; j < arr1.length; j ) {
if (arr2[i] == arr1[j] && !map.containsKey(j)) {
stringArr[i] = pre;
finded = true;
map.put(j, j);
break;
}
}
if (!finded) stringArr[i] = abs;
}
}
}
for (String s : stringArr) {
System.out.println(s);
}
}
}
CodePudding user response:
You can use an int array (or a HashMap<Char, Integer>) count
to store those "presented" characters.
We need two for loops. The first loop will set all "correct" positions (if arr1[i] == arr2[i]
, then stringArr[i] = cor
); and for those unmatched positions (arr1[i] != arr2[i]
), we count the number of occurrences for those characters in the hidden word (count[arr1[i] - 'A']
).
Then in the second for loop, we check those unmatched positions with the help of count
.
- If
count[arr2[i] - 'A'] > 0
, it means that the hidden word contains the characterarr2[i]
, so we can setstringArr[i] = pre
and decrementcount[arr2[i] - 'A']
(since each letter can be used once) - Otherwise, there's no matching letter in the hidden word,
stringArr[i]
should be set toabs
.
import java.util.Scanner;
public class Task1 {
private final static String cor = "correct";
private final static String abs = "absent";
private final static String pre = "present";
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
String a = sc.nextLine();
String b = sc.nextLine();
String[] stringArr = new String[a.length()];
int length1 = a.length();
char[] arr1 = a.toCharArray();
char[] arr2 = b.toCharArray();
int[] count = new int[26];
for (int i = 0; i < length1; i ) {
if (arr2[i] == arr1[i]) {
stringArr[i] = cor;
}
else {
count[arr1[i] - 'A'] ;
}
}
for (int i = 0; i < length1; i ) {
if (arr1[i] != arr2[i]) {
if (count[arr2[i] - 'A'] > 0) {
stringArr[i] = pre;
count[arr2[i] - 'A']--;
}
else {
stringArr[i] = abs;
}
}
}
for (String s : stringArr) {
System.out.println(s);
}
}
}
CodePudding user response:
This can be done using hashmaps and taking advantage of the o(1) insert/delete time.
I'm assuming that each word is exactly the same length. Consequently, to get the answer, we need to check each letter in each word. If they have a length of n
, then that means at a minimum it will take o(2n)
== o(n)
to find the answer.
[1] Then, if you're able to use extra space, I would just use a hashmap as an index, where the key is a character in secret
, and the value is the set of indices that letter occurs in secret
. So it would look something like this:
//cover
c:<0>
o:<1>
etx...
[2] Then check each letter in guess
against the index.
- If there is no key for a letter, the put
absent
- If there is a key, then look in the set of indices to see if there is a matching index. Since I used a hashset, that's another
o(1)
lookup
So sum total, you walk through secret
and guess
once each which is o(n)
runtime. That, plus o(1)
for each index lookup produces o(n)
runtime.
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
public class Task1 {
public static void main(String[] args)
{
ArrayList<String> res = solve("COVER", "CLEAR");
System.out.println(res);
}
public static ArrayList<String> solve(String secret, String guess){
ArrayList<String> result = new ArrayList<>();
HashMap<Character, HashSet<Integer>> index = new HashMap<>();
//shortcut
if(secret.equals(guess)){
for(int i = 0; i < secret.length(); i ) result.add("correct");
return result;
}
//make the index of char:<indices> for the chars in "secret"
for(int i = 0; i < secret.length(); i ){
char c = secret.charAt(i);
HashSet<Integer> bucket = index.get(c);
if(bucket == null){
bucket = new HashSet<>();
index.put(c, bucket);
}
bucket.add(i);
}
//check each char in "guess" against the index and tally the result
for(int i = 0; i < guess.length(); i ){
char c = guess.charAt(i);
HashSet<Integer> bucket = index.get(c);
if(bucket == null) result.add("absent");
else if(bucket.contains(i)) result.add("correct");
else result.add("present");
}
return result;
}
}