Tests are failing, what am I doing wrong? - java

So, I've been trying to get this program to pass both test cases. I'm supposed to make a list that contains the character that follows each non-tail occurrence of a pattern in a text.
Here is the code...
public static ArrayList<Character> getCharsThatFollowPattern (String text, String pattern)
{
ArrayList<Character> character = new ArrayList<Character>();
int i = 0;
while (i <= text.length())
{
int index = text.indexOf(pattern, i);
if (index + pattern.length() < text.length())
{
character.add(text.charAt(index + pattern.length()));
}
i = i + text.indexOf(pattern, index) + pattern.length();
}
return character;
}
Here are the test cases:
a) For this one, I get [b], but I'm supposed to get [b, b]
#Test(timeout = 2000)
public void testGetCharsThatFollowPattern ()
{
ArrayList<Character> list = new ArrayList<Character>();
list.add('b');
list.add('b');
ArrayList<Character> chars = PS5Library.getCharsThatFollowPattern("abababa", "aba");
assertEquals(list, chars);
}
b) For this one, I get [c, d] but I'm supposed to get [c, d, c]
#Test(timeout = 2000)
public void testGetCharsThatFollowPattern2 ()
{
ArrayList<Character> list = new ArrayList<Character>();
list.add('c');
list.add('d');
list.add('c');
ArrayList<Character> chars = PS5Library.getCharsThatFollowPattern("abcabdabcab", "ab");
assertEquals(list, chars);
}

Change your loop as follows:
public static ArrayList<Character> getCharsThatFollowPattern (String text, String pattern) {
ArrayList<Character> characters = new ArrayList<Character>();
int index = text.indexOf(pattern);
while (index >= 0) {
if (index + pattern.length() < text.length()) {
characters.add(text.charAt(index + pattern.length()));
}
index = text.indexOf(pattern, index + 1);
}
return characters;
}
index will return -1 when the pattern is not found any more, so you could use that as your check to exit the loop.
The second indexOf() is only doing + 1 due to the overlap you are expecting for abababawith pattern aba to return b,b.

The problem is that your code finds all separate occurrences (which is normally what would be desired) but what you actually want is to find all occurrences including those that overlap. To do this, you need to make sure that you only move by one character on each iteration instead of moving by the length of the match.
public static ArrayList<Character> getCharsThatFollowPattern (String text, String pattern) {
List<Character> characters = new ArrayList<>();
for (int i = 0; i < text.length(); i++) {
i = text.indexOf(pattern, i);
if (i < 0) break;
int charIndex = i + pattern.length();
if (charIndex >= text.length()) break;
characters.add(text.charAt(charIndex));
}
return characters;
}

Related

Java Program to sort string of arrays

public class App {
public static void main(String[] args) {
getWords("i m very very happy");
}
private static TreeSet<String> getWords(String str) {
char a[] = str.toCharArray();
String smallWords = "";
List<String> words = new ArrayList<>();
for (int i = 0; i < a.length; i++) {
if (a[i] != ' ') {
smallWords = smallWords + a[i];
} else {
char sortedA[] = smallWords.toCharArray();
Arrays.sort(sortedA);
String sortedString = new String(sortedA);
words.add(sortedString);
smallWords = "";
}
}
System.out.println("words :: " + words);
TreeSet<String> sortedWords = new TreeSet<>(words);
System.out.println("sortedWords :: " + sortedWords);
return sortedWords;
}
}
In above code I was able to code the right output with every word sorted of a string array but my output was [ervy, i, m] where last word happy is missing. Can someone please guide what logic I have to use so last word of the array should also reflect in my output.
As #sprinter commented: "he code that adds sortedString to words is only executed when you reach a space in the string. Given there's no space at the end of the string it's never executed for the final word."
One option to fix that would be to break your if statement up into the piece that handles spaces and the piece that processes the words.:
for (int i=0; i < a.length; i++) {
// if not space add to smallWords
if (a[i] != ' ') {
smallWords = smallWords + a[i];
}
// if space or last character, process smallWords
if (a[i] == ' ' || i==a.length-1) {
char sortedA[] = smallWords.toCharArray();
Arrays.sort(sortedA);
String sortedString = new String(sortedA);
words.add(sortedString);
smallWords = "";
}
}
result:
words :: [i, m, ervy, ervy, ahppy]
sortedWords :: [ahppy, ervy, i, m]

Java Programming: Replace all but first and last letters of each word with "_"

The purpose of this method is replace all but the first and last letters of each word with "_". I'm a complete novice when it comes to coding, so I'm certain my code is fairly incorrect. I think where my code starts functioning improperly is with the while loop.
EDIT: How do I make this method without using arrays or extra methods, like the split method?
public static String blankWords(String s1) {
StringBuilder sb = new StringBuilder();
if(s1.length() > 2) {
sb.append(s1.charAt(0));
for(int x = 1; x < s1.length() - 1; x = x + 1) {
char y = ' ';
while(y != s1.charAt(x)) {
sb.append("_");
x = x + 1;
}
}
sb.append(s1.charAt(s1.length() - 1));
return sb.toString();
}
return s1;
}
What my code is outputting:
HW2.blankWords("This is a Test.")
java.lang.StringIndexOutOfBoundsException: String index out of range: 15
at java.lang.String.charAt(Unknown Source)
at HW2.blankWords(HW2.java:73)
What my code should output:
HW2.blankWords("This is a Test.")
"T__s is a T__t."
Here is a pretty simple solution:
class Scratch {
public static void main(String[] args) {
System.out.println(blankWords("My name is sam orozco"));
}
public static String delim = "_";
public static String blankWords(String s1) {
// this split arg on one or more space
String[] words = s1.split("\\s+");
StringBuilder response = new StringBuilder();
for (String val : words) {
val = convertWord(val);
response.append(val).append(" ");
}
return response.toString().trim();
}
public static String convertWord(String val) {
int len = val.length();
StringBuilder bldr = new StringBuilder();
int index = 0;
for (char ch : val.toCharArray()) {
if (index == 0 || index == len - 1) {
bldr.append(ch);
} else {
bldr.append(delim);
}
index++;
}
return bldr.toString();
}
}
You can do this using a StringTokenizer that will extract words based on a list of delimiters. Since you want to keep those delimiters in the output, you'll instruct the tokenizer to return them as tokens:
String blankWords(String s) {
// build a tokenizer for your string, listing all special chars as delimiters. The last argument says that delimiters are going to be returned as tokens themselves (so we can include them in the output string)
StringTokenizer tokenizer = new StringTokenizer(s, " .,;:?!()[]{}", true);
// a helper class to build the output string; think of it as just a more efficient concat utility
StringBuilder sb = new StringBuilder();
while (tokenizer.hasMoreTokens()) {
String blankWord = blank(tokenizer.nextToken());
sb.append(blankWord);
}
return sb.toString();
}
/**
* Replaces all but the first and last characters in a string with '_'
*/
private String blank(String word) {
// strings of up to two chars will be returned as such
// delimiters will always fall into this category, as they are always single characters
if (word.length() <= 2) {
return word;
}
// no need to iterate through all chars, we'll just get the array
final char[] chars = word.toCharArray();
// fill the array of chars with '_', starting with position 1 (the second char) up to the last char (exclusive, i.e. last-but-one)
Arrays.fill(chars, 1, chars.length - 1, '_');
// build the resulting word based on the modified array of chars
return new String(chars);
}
Here is the contents of a test that validates this implementation, using TestNG:
#Test(dataProvider = "texts")
public void testBlankWords(String input, String expectedOutput) {
assertEquals(blankWords(input), expectedOutput);
}
#DataProvider
public Object[][] texts() {
return new Object[][] {
{"This is a test.", "T__s is a t__t."},
{"This one, again, is (yet another) test!", "T__s o_e, a___n, is (y_t a_____r) t__t!"}
};
}
The main drawback of this implementation is that StringTokenizer requires you to list all the delimiters by hand. With a more advanced implementation, you can consider a delimiter any character that returns false for Character.isAlphabetic(c) or however you decide to define your non-word chars.
P.S.
This could be a "more advanced implementation", as I mentioned above:
static String blankWords(String text) {
final char[] textChars = text.toCharArray();
int wordStart = -1; // keep track of the current word start position, -1 means no current word
for (int i = 0; i < textChars.length; i++) {
if (!Character.isAlphabetic(textChars[i])) {
if (wordStart >= 0) {
for (int j = wordStart + 1; j < i - 1; j++) {
textChars[j] = '_';
}
}
wordStart = -1; // reset the current word to none
} else if (wordStart == -1) {
wordStart = i; // alphabetic characters start a new word, when there's none started already
} else if (i == textChars.length - 1) { // if the last character is aplhabetic
for (int j = wordStart + 1; j < i; j++) {
textChars[j] = '_';
}
}
}
return new String(textChars);
}
No while loop necessary!
Look ahead by 1 character to see if it's a space, or if the current character is a space, in that case you append it. Otherwise you make sure to add the next character (skipNext false).
Always add the last character
public static String blankWords(String s1) {
StringBuilder sb = new StringBuilder();
if(s1.length() > 2) {
Boolean skipNext = false;
for(int x = 0; x < s1.length() - 1; x = x + 1) {
if(s1.charAt(x) == ' ' || s1.charAt(x + 1) == ' ') {
sb.append(s1.charAt(x));
skipNext = false;
}
else {
if(skipNext) {
sb.append('_');
}
else {
sb.append(s1.charAt(x));
skipNext = true;
}
}
}
sb.append(s1.charAt(s1.length() - 1));
return sb.toString();
}
return s1;
}
For the more advanced programmer, use regular expression.
public static String blankWords(String s1) {
return s1.replaceAll("\\B\\w\\B", "_");
}
This correctly keeps the final t, i.e. blankWords("This is a Test.") returns "T__s is a T__t.".

Common characters in n strings

I m trying to make a function that prints the number of characters common in given n strings. (note that characters may be used multiple times)
I am struggling to perform this operation on n strings However I did it for 2 strings without any characters repeated more than once.
I have posted my code.
public class CommonChars {
public static void main(String[] args) {
String str1 = "abcd";
String str2 = "bcde";
StringBuffer sb = new StringBuffer();
// get unique chars from both the strings
str1 = uniqueChar(str1);
str2 = uniqueChar(str2);
int count = 0;
int str1Len = str1.length();
int str2Len = str2.length();
for (int i = 0; i < str1Len; i++) {
for (int j = 0; j < str2Len; j++) {
// found match stop the loop
if (str1.charAt(i) == str2.charAt(j)) {
count++;
sb.append(str1.charAt(i));
break;
}
}
}
System.out.println("Common Chars Count : " + count + "\nCommon Chars :" +
sb.toString());
}
public static String uniqueChar(String inputString) {
String outputstr="",temp="";
for(int i=0;i<inputstr.length();i++) {
if(temp.indexOf(inputstr.charAt(i))<0) {
temp+=inputstr.charAt(i);
}
}
System.out.println("completed");
return temp;
}
}
3
abcaa
bcbd
bgc
3
their may be chances that a same character can be present multiple times in
a string and you are not supposed to eliminate those characters instead
check the no. of times they are repeated in other strings. for eg
3
abacd
aaxyz
aatre
output should be 2
it will be better if i get solution in java
You have to convert all Strings to Set of Characters and retain all from the first one. Below solution has many places which could be optimised but you should understand general idea.
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
public class Main {
public static void main(String[] args) {
List<String> input = Arrays.asList("jonas", "ton", "bonny");
System.out.println(findCommonCharsFor(input));
}
public static Collection<Character> findCommonCharsFor(List<String> strings) {
if (strings == null || strings.isEmpty()) {
return Collections.emptyList();
}
Set<Character> commonChars = convertStringToSetOfChars(strings.get(0));
strings.stream().skip(1).forEach(s -> commonChars.retainAll(convertStringToSetOfChars(s)));
return commonChars;
}
private static Set<Character> convertStringToSetOfChars(String string) {
if (string == null || string.isEmpty()) {
return Collections.emptySet();
}
Set<Character> set = new HashSet<>(string.length() + 10);
for (char c : string.toCharArray()) {
set.add(c);
}
return set;
}
}
Above code prints:
[n, o]
A better strategy for your problem is to use this method:
public int[] countChars(String s){
int[] count = new int[26];
for(char c: s.toCharArray()){
count[c-'a']++;
}
return count;
}
Now if you have n Strings (String[] strings) just find the min of common chars for each letter:
int[][] result = new int[n][26]
for(int i = 0; i<strings.length;i++){
result[i] = countChars(s);
}
// now if you sum the min common chars for each counter you are ready
int commonChars = 0;
for(int i = 0; i< 26;i++){
int min = result[0][i];
for(int i = 1; i< n;i++){
if(min>result[j][i]){
min = result[j][i];
}
}
commonChars+=min;
}
Get list of characters for each string:
List<Character> chars1 = s1.chars() // list of chars for first string
.mapToObj(c -> (char) c)
.collect(Collectors.toList());
List<Character> chars2 = s2.chars() // list of chars for second string
.mapToObj(c -> (char) c)
.collect(Collectors.toList());
Then use retainAll method:
chars1.retainAll(chars2); // retain in chars1 only the chars that are contained in the chars2 also
System.out.println(chars1.size());
If you want to get number of unique chars just use Collectors.toSet() instead of toList()
Well if one goes for hashing:
public static int uniqueChars(String first, String second) {
boolean[] hash = new boolean[26];
int count = 0;
//reduce first string to unique letters
for (char c : first.toLowerCase().toCharArray()) {
hash[c - 'a'] = true;
}
//reduce to unique letters in both strings
for(char c : second.toLowerCase().toCharArray()){
if(hash[c - 'a']){
count++;
hash[c - 'a'] = false;
}
}
return count;
}
This is using bucketsort which gives a n+m complexity but needs the 26 buckets(the "hash" array).
Imo one can't do better in regards of complexity as you need to look at every letter at least once which sums up to n+m.
Insitu the best you can get is imho somewhere in the range of O(n log(n) ) .
Your aproach is somewhere in the league of O(n²)
Addon: if you need the characters as a String(in essence the same as above with count is the length of the String returned):
public static String uniqueChars(String first, String second) {
boolean[] hash = new boolean[26];
StringBuilder sb = new StringBuilder();
for (char c : first.toLowerCase().toCharArray()) {
hash[c - 'a'] = true;
}
for(char c : second.toLowerCase().toCharArray()){
if(hash[c - 'a']){
sb.append(c);
hash[c - 'a'] = false;
}
}
return sb.toString();
}
public static String getCommonCharacters(String... words) {
if (words == null || words.length == 0)
return "";
Set<Character> unique = words[0].chars().mapToObj(ch -> (char)ch).collect(Collectors.toCollection(TreeSet::new));
for (String word : words)
unique.retainAll(word.chars().mapToObj(ch -> (char)ch).collect(Collectors.toSet()));
return unique.stream().map(String::valueOf).collect(Collectors.joining());
}
Another variant without creating temporary Set and using Character.
public static String getCommonCharacters(String... words) {
if (words == null || words.length == 0)
return "";
int[] arr = new int[26];
boolean[] tmp = new boolean[26];
for (String word : words) {
Arrays.fill(tmp, false);
for (int i = 0; i < word.length(); i++) {
int pos = Character.toLowerCase(word.charAt(i)) - 'a';
if (tmp[pos])
continue;
tmp[pos] = true;
arr[pos]++;
}
}
StringBuilder buf = new StringBuilder(26);
for (int i = 0; i < arr.length; i++)
if (arr[i] == words.length)
buf.append((char)('a' + i));
return buf.toString();
}
Demo
System.out.println(getCommonCharacters("abcd", "bcde")); // bcd

Find a word in char array java

I'm trying to find a word from a dictonary list. The letters can be in any order, but any letter can be used only once. I already developed an algorithm in java on Android, but it doesn't really work.
--> all words in the dict list are already lowercased in my cause
Here is my existing code, but it won't show me the matching words as output, the returned list is always empty.
private int matches = 0;
private ArrayList<String> words;
private ArrayList<String> check(String charArr0) {
String charArr = charArr0.toLowerCase();
char[] cs0 = charArr.toCharArray();
ArrayList<Character> cs = new ArrayList<>();
for(char c : cs0)
cs.add(c);
all = words.size();
ArrayList<String> out = new ArrayList<>();
for(String w0 : words) {
String w = w0.toLowerCase();
int len = w.length();
if(len >= 2) {
//only if len is 2++
matches = 0;
checkNext(cs, 0, w, len);
//if matches are as high as words lenght, it is fully avaivable
if(matches >= len)
out.add(w);
}
}
return out;
}
private void checkNext(ArrayList<Character> cs, int pos, String w, int len) {
if(pos < len) {
char twc = w.charAt(pos);
boolean cont = false;
int cIdx = -1, curi = 0;
for(char c : cs) {
if(c == twc){
cont = true;
cIdx = curi;
break;
}
curi += 1;
}
if(cont) {
matches += 1;
cs.remove(cIdx);
checkNext(cs, pos + 1, w, len);
}
}
}
The question is, what the error in this code is and how could I possibly get a word from the list in a char array given (any char only used once, order doesn't matter)?
Because you define this rules :
//- The letters can be in any order
//- any letter can be used only once
I would like to sort the character of each word and check if they are equals or not :
List<String> dictionaryWords = ...;
String word = "word";
char[] wordChars = word.toCharArray();
Arrays.sort(wordChars);
List<String> foundWords = new ArrayList<>();
for(String w : dictionaryWords){
if(dictionaryWords.length() != wordChars.length)
continue;
char[] wordDictionaryChars = w.toCharArray();
Arrays.sort(wordDictionaryChars);
if(Arrays.equals(wordChars, wordDictionaryChars)){
foundWords.add(w);
}
}
Consider you have :
List<String> dictionaryWords = new ArrayList<>(Arrays.asList("drow", "hello"));
This will return :
[drow]
because when you order both word and drow it will gives you [d, o, r, w]

Minimum window in String 1 containing all characters from String 2 and no character from String 3

Ok, this is an interview question. And no it's not a duplicate of this question.
Given 3 strings - str1, str2, str3:
str1 = "spqrstrupvqw"
str2 = "sprt"
str3 = "q"
We've to find the minimum window in str1, which contains all characters from str2 in any order, but no character from str3. In this case the answer would be: "strup".
I've come up with this code:
static String minimumWindow(String str1, String str2, String str3) {
class Window implements Comparable<Window> {
int start;
int end;
public Window(int start, int end) {
this.start = start;
this.end = end;
}
public int getEnd() {
return end;
}
public int getStart() {
return start;
}
public int compareTo(Window o) {
int thisDiff = end - start;
int thatDiff = o.end - o.start;
return Integer.compare(thisDiff, thatDiff);
}
#Override
public String toString() {
return "[" + start + " : " + end + "]";
}
}
// Create Sets of characters for "contains()" check
Set<Character> str2Chars = new HashSet<>();
for (char ch: str2.toCharArray()) {
str2Chars.add(ch);
}
Set<Character> str3Chars = new HashSet<>();
for (char ch: str3.toCharArray()) {
str3Chars.add(ch);
}
// This will store all valid window which doesn't contain characters
// from str3.
Set<Window> set = new TreeSet<>();
int begin = -1;
// This loops gets each pair of index, such that substring from
// [start, end) in each window doesn't contain any characters from str3
for (int i = 0; i < str1.length(); i++) {
if (str3Chars.contains(str1.charAt(i))) {
set.add(new Window(begin, i));
begin = i + 1;
}
}
int minLength = Integer.MAX_VALUE;
String minString = "";
// Iterate over the windows to find minimum length string containing all
// characters from str2
for (Window window: set) {
if ((window.getEnd() - 1 - window.getStart()) < str2.length()) {
continue;
}
for (int i = window.getStart(); i < window.getEnd(); i++) {
if (str2Chars.contains(str1.charAt(i))) {
// Got first character in this window that is in str2
// Start iterating from end to get last character
// [start, end) substring will be the minimum length
// string in this window
for (int j = window.getEnd() - 1; j > i; j--) {
if (str2Chars.contains(str1.charAt(j))) {
String s = str1.substring(i, j + 1);
Set<Character> sChars = new HashSet<>();
for (char ch: s.toCharArray()) {
sChars.add(ch);
}
// If this substring contains all characters from str2,
// then only it is valid window.
if (sChars.containsAll(str2Chars)) {
int len = sChars.size();
if (len < minLength) {
minLength = len;
minString = s;
}
}
}
}
}
}
}
// There are cases when some trailing and leading characters are
// repeated somewhere in the middle. We don't need to include them in the
// minLength.
// In the given example, the actual string would come as - "rstrup", but we
// remove the first "r" safely.
StringBuilder strBuilder = new StringBuilder(minString);
while (strBuilder.length() > 1 && strBuilder.substring(1).contains("" + strBuilder.charAt(0))) {
strBuilder.deleteCharAt(0);
}
while (strBuilder.length() > 1 && strBuilder.substring(0, strBuilder.length() - 1).contains("" + strBuilder.charAt(strBuilder.length() - 1))) {
strBuilder.deleteCharAt(strBuilder.length() - 1);
}
return strBuilder.toString();
}
But it doesn't work for all the test cases. It does work for the example given in this question. But when I submitted the code, it failed for 2 test cases. No I don't know the test cases for which it failed.
Even after trying various sample inputs, I couldn't find a test case for which it fails. Can someone take a look as to what is wrong with the code? I would really appreciate if someone can give a better algorithm (Just in pseudo-code). I know this is really not the optimized solution though.
str1 = "spqrstrupvqw"
str2 = "sprt"
str3 = "q"
We're looking for the minimum sub-string from str1 that contain all str2 characters (assume ordered) and no characters from str3 ..
i = 1 .. str1.length
cursor = 1 .. str2.length
The solution must be on the form:
str2.first X X .. X X str2.last
So to check for that sub-string we use a cursor over str2, but we also have the constraint of avoiding str3 characters, so we have:
if str3.contain(str1[i])
cursor = 1
else
if str1[i] == str2[cursor]
cursor++
Goal check is:
if cursor > str2.length
return solution
else
if i >= str1.length
return not-found
And for optimization, you can skip to the next look-ahead which is:
look-ahead = { str2[cursor] or { X | X in str3 }}
In case str2 is not ordered:
i = 1 .. str1.length
lookup = { X | X in str2 }
The solution must be on the form:
str2[x] X X .. X X str2[x]
So to check for that sub-string we use a check-list str2, but we also have the constraint of avoiding str3 characters, so we have:
if str3.contain(str1[i])
lookup = { X | X in str2 }
else
if lookup.contain(str1[i])
lookup.remove(str1[i])
Goal check is:
if lookup is empty
return solution
else
if i >= str1.length
return not-found
And for optimization, you can skip to the next look-ahead which is:
look-ahead = {{ X | X in lookup } or { X | X in str3 }}
Code
class Solution
{
private static ArrayList<Character> getCharList (String str)
{
return Arrays.asList(str.getCharArray());
}
private static void findFirst (String a, String b, String c)
{
int cursor = 0;
int start = -1;
int end = -1;
ArrayList<Character> stream = getCharList(a);
ArrayList<Character> lookup = getCharList(b);
ArrayList<Character> avoid = getCharList(c);
for(Character ch : stream)
{
if (avoid.contains(ch))
{
lookup = getCharList(b);
start = -1;
end = -1;
}
else
{
if (lookup.contains(ch))
{
lookup.remove(ch)
if (start == -1) start = cursor;
end = cursor;
}
}
if (lookup.isEmpty())
break;
cursor++;
}
if (lookup.isEmpty())
{
System.out.println(" found at ("+start+":"+end+") ");
}
else
{
System.out.println(" not found ");
}
}
}
Here is working Java code tested on various test cases.
The algorithm basically uses a sliding window to examine different windows within which an answer may lie. Each character in the string str2 is analyzed at most twice. Thus the running time of the algorithm is linear, ie O(N) in the lengths of the three strings. This is infact the most optimal solution for this problem.
String str1 = "spqrstrupvqw";
String str2 = "sprt";
String str3 = "q";
char[] arr = str1.toCharArray();
HashSet<Character> take = new HashSet<Character>();
HashSet<Character> notTake = new HashSet<Character>();
HashMap<Character, Integer> map = new HashMap<Character, Integer>();
void run()throws java.lang.Exception{
System.out.println(str1 + " " + str2 + " " + str3);
//Add chars of str2 to a set to check if a char has to be taken in O(1)time.
for(int i=0; i<str2.length(); i++){
take.add(str2.charAt(i));
}
//Add chars of str3 to a set to check if a char shouldn't be taken in O(1) time.
for(int i=0; i<str3.length(); i++){
notTake.add(str3.charAt(i));
}
int last = -1;
int bestStart = -1;
int bestLength = arr.length+1;
// The window will be from [last....next]
for(int next=last+1; next<arr.length; next++){
if(notTake.contains(arr[next])){
last = initLast(next+1); //reinitialize the window's start.
next = last;
}else if(take.contains(arr[next])){
// take this character in the window and update count in map.
if(last == -1){
last = next;
map.put(arr[last], 1);
}else{
if(!map.containsKey(arr[next])) map.put(arr[next], 1);
else map.put(arr[next], map.get(arr[next])+1);
}
}
if(last >= arr.length){ // If window is invalid
break;
}
if(last==-1){
continue;
}
//shorten window by removing chars from start that are already present.
while(last <= next){
char begin = arr[last];
// character is not needed in the window, ie not in set "take"
if(!map.containsKey(begin)){
last++;
continue;
}
// if this character already occurs in a later part of the window
if(map.get(begin) > 1){
last++;
map.put(begin, map.get(begin)-1);
}else{
break;
}
}
// if all chars of str2 are in window and no char of str3 in window,
// then update bestAnswer
if(map.size() == str2.length()){
int curLength = next - last + 1;
if(curLength < bestLength){
bestLength = curLength;
bestStart = last;
}
}
}
if(bestStart==-1){
System.out.println("there is no such window");
}else{
System.out.println("the window is from " + bestStart + " to " + (bestStart + bestLength-1));
System.out.println("window " + str1.substring(bestStart, bestStart+bestLength));
}
}
// Returns the first position in arr starting from index 'fromIndex'
// such that the character at that position is in str2.
int initLast(int fromIndex){
// clear previous mappings as we are starting a new window
map.clear();
for(int last=fromIndex; last<arr.length; last++){
if(take.contains(arr[last])){
map.put(arr[last], 1);
return last;
}
}
return arr.length;
}
Moreover, your code fails on many trivial test cases. One of them is when str1 = "abc", str2 = "ab", str3 = "c".
PS. If you are having a hard time understanding this code, first try reading this easier post which is very similar to the problem that has been asked.
What about using a regular expression?
String regex = ".*((?=[^q]*s)(?=[^q]*p)(?=[^q]*r)(?=[^q]*t)[sprt][^q]+([sprt])(?<!ss|pp|rr|tt))";
Matcher m = Pattern.compile(regex).matcher("spqrstrupvqw");
while (m.find()) {
System.out.println(m.group(1));
}
This prints out:
strup
This can also be wrapped in a method which generates dynamically the regular expression for variable inputs:
import java.util.ArrayList;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class MatchString {
public static void main(String[] args) {
System.out.println(getMinimalSubstrings("spqrstrupvqw", "sprt", "q"));
System.out.println(getMinimalSubstrings("A question should go inside quotations.", "qtu", "op"));
System.out.println(getMinimalSubstrings("agfbciuybfac", "abc", "xy"));
}
private static List<String> getMinimalSubstrings(String input, String mandatoryChars, String exceptChars) {
List<String> list = new ArrayList<String>();
String regex = buildRegEx(mandatoryChars, exceptChars);
Matcher m = Pattern.compile(regex).matcher(input);
while (m.find()) {
list.add(m.group(1));
}
return list;
}
private static String buildRegEx(String mandatoryChars, String exceptChars) {
char[] mandChars = mandatoryChars.toCharArray();
StringBuilder regex = new StringBuilder("[^").append(exceptChars).append("]*(");
for (char c : mandChars) {
regex.append("(?=[^").append(exceptChars).append("]*").append(c).append(")");
}
regex.append("[").append(mandatoryChars).append("][^").append(exceptChars).append("]+([").append(mandatoryChars).append("])(?<!");
for (int i = 0; i < mandChars.length; i++) {
if (i > 0) {
regex.append("|");
}
regex.append(mandChars[i]).append(mandChars[i]);
}
regex.append("))");
return regex.toString();
}
}
This prints out:
[strup]
[quest]
[agfbc, bfac]

Categories