Write a method to test if a string meets the preconditions to become a palindrome.
Eg:
Input | Output
mmo | True
yakak | True
travel | False
I'm thinking of this approach:
Make a suffix tree for all permutation of T such that T$Reverse(T)#
Check for all permutation for same node
Am I missing anything?
All you need to do is check that there's at most one character with an odd number of occurrences. Here's a Java example:
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 (yes - these are ordered to make sense, but numbered by chronological order):
The above implementation has a built in inefficiency. I don't think the first iteration over the string can be avoided, but there's no real reason to keep a count of all the occurrences - it's enough to just keep track of those with the an odd count. For this usecase, it's enough to keep track of each character we encounter (e.g., with a Set), and remove it when we encounter it again. In the worst case, where all the characters in the string are different, the performance is comparable, but in the common case, where there are several occurrences of each character, this implementation improves both time and memory complexity of the second loop (which is now reduced to a single condition) dramatically:
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 (yes - these are ordered to make sense, but numbered by chronological order):
Java 8 provides a fluent streaming API which could be used to create an implementation similar to the Python one-liners below:
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;
}
EDIT:
Python built-in functions and comprehension capabilities make this too attractive not to publish this one liner solution. It's probably less efficient than the aforementioned Java one, but is quite elegant:
from collections import Counter
def canMakePalindrom(s):
return len([v for v in Counter(s).values() if v % 2 == 1]) <= 1
EDIT2:
Or, an even cleaner approach as proposed by #DSM in the comments:
from collections import Counter
def canMakePalindrom(s):
return sum(v % 2 == 1 for v in Counter(s).values()) <= 1
Instead of counting how many times each letter occurs, another approach keeps track of whether a letter has occurred an odd or even number of times. If a letter has occurred an even number of times, you don’t need to worry about it, and only need to keep track of the odd occurrences in a set. In 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;
}
Really all you're looking for is if all (or all but one) of the letters are paired off. As long as they are, then they will be able to be turned into a palindrome.
So it would be something like...
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;
}
This isn't the most efficient (it's counting letters as many times as it comes across them, even if they've been counted already) but it does work.
If I'm understanding your question correctly, this is how I understand it:
If the input string can be rearranged into a palindrome, output "True", otherwise output "False".
Then you can use these simple rules:
If the length is even, every unique character in the input has to occur a multiple of 2 times.
If the length is odd, every unique character except one has to occur a multiple of 2 times. Only 1 character is allowed to not occur a multiple of 2 times.
So for the 3 given examples:
"mmo", odd length, m occurs twice (multiple of 2), o occurs once (not a multiple of 2), so True.
"yakak", odd length, a occurs twice (multiple of 2), k occurs twice (multiple of 2), y occurs once (not a multiple of 2) , so True.
"travel", more than one character does not occur a multiple of 2, so False.
Additional examples:
"mmorpg", only m occurs a multiple of 2, the rest only once, so False.
"mmom", no characters occur a multiple of 2, more than one character occurs "not a multiple of 2 times", so False.
At this point you should realise that if only 1 character is allowed to occur a non-multiple-of-2 times, then you can disregard the length. A string with an even length will have either 2 or more characters occuring a non-multiple-of-2 times, or none at all.
So the final rule should be this:
If at most 1 unique character occurs a non-multiple-of-2 times in the input, the output is True otherwise the output is False.
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]
def check(string):
bv = 0
for s in string:
bv ^= 1 << ord(s)
return bv == 0 or bv & (bv - 1) == 0
I reached the solution below today (python). I think it's readable, and performance-wise it's really good.
sum(map(lambda x: word.count(x) % 2, set(word))) <= 1
We're basically counting the number of occurrences of each character in the string "word", getting the remainder of the division by 2, summing them all and checking if you have at most 1 of them.
The idea is that you need to have all characters paired, except potentially for one (the middle one).
My idea is, if the number of letters with odd count is one and rest all have even count, a palindrome is possible..Here's my program in 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")
Any string can be palindrome only if at most one character occur odd no. of times and all other characters must occur even number of times. The following program can be used to check whether a palindrome can be string or not.
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;
}
With O(n) complexity .
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();
}
}
}
If we don't care case sensitivity of characters and spaces within a string, then a sample solution in C# by using Dictionary can be like :
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;
}
We can acheive this via collections also
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");
}
}
This is my solution
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");
}
}
That 's my solution. The string could contain several words with spaces, such as
Input: Tact Coa
Output true
Input: Tact Coa vvu
Output: false
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;
}
Question: Can a String become a palindrome?
Method1: count of characters
IN 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");
}
}
}
IN 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)
Method 2 : Use of HashSet
In 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");
}
}
}
Swift example for this question.
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))
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;
}
}
}
Why use a suffix tree or any other data structure?
The basic requirement of a palindromic string is the frequency of all characters must be even or only one character can have odd frequency.
Example :-
Input : aabbaa
Output : frequency of a is 4 and b is 2 (both even)
Input : xxzyzxx
Output : frequency of x is 4, z is 2 and y=1 (only 1 odd)
Sample code for better understanding :
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
}
python code to check whether a palindrome can be formed from given string or not:
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")
Please try this code if any issue please comments
More efficient implementation - 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);
}
Here is my code :
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;
}
JS solution:
function solution(inputString) {
const arr = inputString.split('');
let hasCoupleList = arr.map( (el) => arr.filter( (el1) => el1 == el).length % 2 == 0).filter( (el) => el == false).length;
return (arr.length % 2 == 0)
? hasCoupleList == 0
: hasCoupleList == 1;
}
With JAVA
import java.util.*;
import java.lang.*;
//Classs
class Permutation {
/*
* We need to have an even number of almost all characters,
* so that half can be on one side and half can be on the other side.
* At most one character (the middle character) can have an odd count.
*/
public static boolean hasPalindrome(String str) {
boolean wasOdd = false;
for (Character c: str.toCharArray()) {
int counter = 0;
for (Character cc: str.toCharArray()) {
if (c == cc) {
counter++;
}
}
if (counter % 2 == 1) {
if (wasOdd) {
return false;
}
wasOdd = true;
}
}
return true;
}
public static void main(String args[]) throws Exception {
//Taking string input
//Scanner
Scanner s = new Scanner(System.in);
String str = s.nextLine();
if (Permutation.hasPalindrome(str)) {
System.out.println("YES"); // Writing output to STDOUT
} else {
System.out.println("NO"); // Writing output to STDOUT
}
}
}
Implementation from Checking if a String is a Permutation of a Palindrome
Time complexity is essentially O(n). This means that the function is linear in the length of the input string
public static boolean isPermutationOfPalindrome(String str) {
// Convert the input string to lower case and remove any non-letter characters
str = str.toLowerCase().replaceAll("[^a-z]", "");
// Create an array to count the frequency of each letter
int[] charCounts = new int[26];
for (int i = 0; i < str.length(); i++) {
charCounts[str.charAt(i) - 'a']++;
}
// Check if there is at most one character with an odd frequency
boolean foundOdd = false;
for (int count : charCounts) {
if (count % 2 == 1) {
if (foundOdd) {
return false;
}
foundOdd = true;
}
}
return true;
}
A friend of mine just had his interview at Google and got rejected because he couldn't give a solution to this question.
I have my own interview in a couple of days and can't seem to figure out a way to solve it.
Here's the question:
You are given a pattern, such as [a b a b]. You are also given a
string, example "redblueredblue". I need to write a program that tells
whether the string follows the given pattern or not.
A few examples:
Pattern: [a b b a] String: catdogdogcat returns 1
Pattern: [a b a b] String: redblueredblue returns 1
Pattern: [a b b a] String: redblueredblue returns 0
I thought of a few approaches, like getting the number of unique characters in the pattern and then finding that many unique substrings of the string then comparing with the pattern using a hashmap. However, that turns out to be a problem if the substring of a is a part of b.
It'd be really great if any of you could help me out with it. :)
UPDATE:
Added Info: There can be any number of characters in the pattern (a-z). Two characters won't represent the same substring. Also, a character can't represent an empty string.
The simplest solution that I can think of is to divide the given string into four parts and compare the individual parts. You don't know how long a or b is, but both as are of the same length as well as bs are. So the number of ways how to divide the given string is not very large.
Example:
pattern = [a b a b], given string = redblueredblue (14 characters in total)
|a| (length of a) = 1, then that makes 2 characters for as and 12 characters is left for bs, i.e. |b| = 6. Divided string = r edblue r edblue. Whoa, this matches right away!
(just out of curiosity) |a| = 2, |b| = 5 -> divided string = re dblue re dblue -> match
Example 2:
pattern = [a b a b], string = redbluebluered (14 characters in total)
|a| = 1, |b| = 6 -> divided string = r edblue b luered -> no match
|a| = 2, |b| = 5 -> divided string = re dblue bl uered -> no match
|a| = 3, |b| = 4 -> divided string = red blue blu ered -> no match
The rest is not needed to be checked because if you switched a for b and vice versa, the situation is identical.
What is the pattern that has [a b c a b c] ?
Don't you just need to translate the pattern to a regexp using backreferences, i.e. something like this (Python 3 with the "re" module loaded):
>>> print(re.match('(.+)(.+)\\2\\1', 'catdogdogcat'))
<_sre.SRE_Match object; span=(0, 12), match='catdogdogcat'>
>>> print(re.match('(.+)(.+)\\1\\2', 'redblueredblue'))
<_sre.SRE_Match object; span=(0, 14), match='redblueredblue'>
>>> print(re.match('(.+)(.+)\\2\\1', 'redblueredblue'))
None
The regexp looks pretty trivial to generate. If you need to support more than 9 backrefs, you can use named groups - see the Python regexp docs.
Here is java backtracking solution. Source link.
public class Solution {
public boolean isMatch(String str, String pat) {
Map<Character, String> map = new HashMap<>();
return isMatch(str, 0, pat, 0, map);
}
boolean isMatch(String str, int i, String pat, int j, Map<Character, String> map) {
// base case
if (i == str.length() && j == pat.length()) return true;
if (i == str.length() || j == pat.length()) return false;
// get current pattern character
char c = pat.charAt(j);
// if the pattern character exists
if (map.containsKey(c)) {
String s = map.get(c);
// then check if we can use it to match str[i...i+s.length()]
if (i + s.length() > str.length() || !str.substring(i, i + s.length()).equals(s)) {
return false;
}
// if it can match, great, continue to match the rest
return isMatch(str, i + s.length(), pat, j + 1, map);
}
// pattern character does not exist in the map
for (int k = i; k < str.length(); k++) {
// create or update the map
map.put(c, str.substring(i, k + 1));
// continue to match the rest
if (isMatch(str, k + 1, pat, j + 1, map)) {
return true;
}
}
// we've tried our best but still no luck
map.remove(c);
return false;
}
}
One more brute force recursion solution:
import java.io.IOException;
import java.util.*;
public class Test {
public static void main(String[] args) throws IOException {
int res;
res = wordpattern("abba", "redbluebluered");
System.out.println("RESULT: " + res);
}
static int wordpattern(String pattern, String input) {
int patternSize = 1;
boolean res = findPattern(pattern, input, new HashMap<Character, String>(), patternSize);
while (!res && patternSize < input.length())
{
patternSize++;
res = findPattern(pattern, input, new HashMap<Character, String>(), patternSize);
}
return res ? 1 : 0;
}
private static boolean findPattern(String pattern, String input, Map<Character, String> charToValue, int patternSize) {
StringBuilder sb = new StringBuilder();
for (int i = 0; i < pattern.length(); i++) {
char c = pattern.charAt(i);
if (charToValue.containsKey(c)) {
sb.append(charToValue.get(c));
} else {
// new character in pattern
if (sb.length() + patternSize > input.length()) {
return false;
} else {
String substring = input.substring(sb.length(), sb.length() + patternSize);
charToValue.put(c, substring);
int newPatternSize = 1;
boolean res = findPattern(pattern, input, new HashMap<>(charToValue), newPatternSize);
while (!res && newPatternSize + sb.length() + substring.length() < input.length() - 1) {
newPatternSize++;
res = findPattern(pattern, input, new HashMap<>(charToValue), newPatternSize);
}
return res;
}
}
}
return sb.toString().equals(input) && allValuesUniq(charToValue.values());
}
private static boolean allValuesUniq(Collection<String> values) {
Set<String> set = new HashSet<>();
for (String v : values) {
if (!set.add(v)) {
return false;
}
}
return true;
}
}
My Implementation on C#. Tried to look for something clean in C#, couldn't find. So I'll add it to here.
private static bool CheckIfStringFollowOrder(string text, string subString)
{
int subStringLength = subString.Length;
if (text.Length < subStringLength) return false;
char x, y;
int indexX, indexY;
for (int i=0; i < subStringLength -1; i++)
{
indexX = -1;
indexY = -1;
x = subString[i];
y = subString[i + 1];
indexX = text.LastIndexOf(x);
indexY = text.IndexOf(y);
if (y < x || indexX == -1 || indexY == -1)
return false;
}
return true;
}
I solved this as a language production problem using regexen.
def wordpattern( pattern, string):
'''
input: pattern 'abba'
string 'redbluebluered'
output: 1 for match, 2 for no match
'''
# assemble regex into something like this for 'abba':
# '^(?P<A>.+)(?P<B>.+)(?P=B)(?P=A)$'
p = pattern
for c in pattern:
C = c.upper()
p = p.replace(c,"(?P<{0}>.+)".format(C),1)
p = p.replace(c,"(?P={0})".format(C),len(pattern))
p = '^' + p + '$'
# check for a preliminary match
if re.search(p,string):
rem = re.match(p,string)
seen = {}
# check to ensure that no points in the pattern share the same match
for c in pattern:
s = rem.group(c.upper())
# has match been seen? yes, fail, no continue
if s in seen and seen[s] != c:
return 0
seen[s] = c
# success
return 1
# did not hit the search, fail
return 0
#EricM
I tested your DFS solution and it seems wrong, like case:
pattern = ["a", "b", "a"], s = "patrpatrr"
The problem is that when you meet a pattern that already exists in dict and find it cannot fit the following string, you delete and try to assign it a new value. However, you haven't check this pattern with the new value for the previous times it occurs.
My idea is about providing addition dict (or merge in this dict) new value to keep track of the first time it appears and another stack to keep track of the unique pattern I meet. when "not match" occurs, I will know there is some problem with the last pattern and I pop it from the stack and modify the corresponding value in the dict, also I will start to check again at that corresponding index. If cannot be modified any more. I will pop until there is none left in the stack and then return False.
(I want to add comments but don't have enough reputation as a new user.. I haven't implement it but till now I haven't find any error in my logic. I am sorry if there is something wrong with my solution== I will try to implement it later.)
I can't think of much better than the brute force solution: try every possible partitioning of the word (this is essentially what Jan described).
The run-time complexity is O(n^(2m)) where m is the length of the pattern and n is the length of the string.
Here's what the code for that looks like (I made my code return the actual mapping instead of just 0 or 1. Modifying the code to return 0 or 1 is easy):
import java.util.Arrays;
import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Deque;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class StringBijection {
public static void main(String[] args) {
String chars = "abaac";
String string = "johnjohnnyjohnjohncodes";
List<String> stringBijection = getStringBijection(chars, string);
System.out.println(Arrays.toString(stringBijection.toArray()));
}
public static List<String> getStringBijection(String chars, String string) {
if (chars == null || string == null) {
return null;
}
Map<Character, String> bijection = new HashMap<Character, String>();
Deque<String> assignments = new ArrayDeque<String>();
List<String> results = new ArrayList<String>();
boolean hasBijection = getStringBijection(chars, string, 0, 0, bijection, assignments);
if (!hasBijection) {
return null;
}
for (String result : assignments) {
results.add(result);
}
return results;
}
private static boolean getStringBijection(String chars, String string, int charIndex, int stringIndex, Map<Character, String> bijection, Deque<String> assignments) {
int charsLen = chars.length();
int stringLen = string.length();
if (charIndex == charsLen && stringIndex == stringLen) {
return true;
} else if (charIndex == charsLen || stringIndex == stringLen) {
return false;
}
char currentChar = chars.charAt(charIndex);
List<String> possibleWords = new ArrayList<String>();
boolean charAlreadyAssigned = bijection.containsKey(currentChar);
if (charAlreadyAssigned) {
String word = bijection.get(currentChar);
possibleWords.add(word);
} else {
StringBuilder word = new StringBuilder();
for (int i = stringIndex; i < stringLen; ++i) {
word.append(string.charAt(i));
possibleWords.add(word.toString());
}
}
for (String word : possibleWords) {
int wordLen = word.length();
int endIndex = stringIndex + wordLen;
if (endIndex <= stringLen && string.substring(stringIndex, endIndex).equals(word)) {
if (!charAlreadyAssigned) {
bijection.put(currentChar, word);
}
assignments.addLast(word);
boolean done = getStringBijection(chars, string, charIndex + 1, stringIndex + wordLen, bijection, assignments);
if (done) {
return true;
}
assignments.removeLast();
if (!charAlreadyAssigned) {
bijection.remove(currentChar);
}
}
}
return false;
}
}
If you are looking for a solution in C++, here is a brute force solution:
https://linzhongzl.wordpress.com/2014/11/04/repeating-pattern-match/
Plain Brute Force, not sure if any optimization is possible here ..
import java.util.HashMap;
import java.util.Map;
import org.junit.*;
public class Pattern {
private Map<Character, String> map;
private boolean matchInt(String pattern, String str) {
if (pattern.length() == 0) {
return str.length() == 0;
}
char pch = pattern.charAt(0);
for (int i = 0; i < str.length(); ++i) {
if (!map.containsKey(pch)) {
String val = str.substring(0, i + 1);
map.put(pch, val);
if (matchInt(pattern.substring(1), str.substring(val.length()))) {
return true;
} else {
map.remove(pch);
}
} else {
String val = map.get(pch);
if (!str.startsWith(val)) {
return false;
}
return matchInt(pattern.substring(1), str.substring(val.length()));
}
}
return false;
}
public boolean match(String pattern, String str) {
map = new HashMap<Character, String>();
return matchInt(pattern, str);
}
#Test
public void test1() {
Assert.assertTrue(match("aabb", "ABABCDCD"));
Assert.assertTrue(match("abba", "redbluebluered"));
Assert.assertTrue(match("abba", "asdasdasdasd"));
Assert.assertFalse(match("aabb", "xyzabcxzyabc"));
Assert.assertTrue(match("abba", "catdogdogcat"));
Assert.assertTrue(match("abab", "ryry"));
Assert.assertFalse(match("abba", " redblueredblue"));
}
}
class StringPattern{
public:
int n, pn;
string str;
unordered_map<string, pair<string, int>> um;
vector<string> p;
bool match(string pat, string str_) {
p.clear();
istringstream istr(pat);
string x;
while(istr>>x) p.push_back(x);
pn=p.size();
str=str_;
n=str.size();
um.clear();
return dfs(0, 0);
}
bool dfs(int i, int c) {
if(i>=n) {
if(c>=pn){
return 1;
}
}
if(c>=pn) return 0;
for(int len=1; i+len-1<n; len++) {
string sub=str.substr(i, len);
if(um.count(p[c]) && um[p[c]].fi!=sub
|| um.count(sub) && um[sub].fi!=p[c]
)
continue;
//cout<<"str:"<<endl;
//cout<<p[c]<<" "<<sub<<endl;
um[p[c]].fi=sub;
um[p[c]].se++;
um[sub].fi=p[c];
um[sub].se++;
//um[sub]=p[c];
if(dfs(i+len, c+1)) return 1;
um[p[c]].se--;
if(!um[p[c]].se) um.erase(p[c]);
um[sub].se--;
if(!um[sub].se) um.erase(sub);
//um.erase(sub);
}
return 0;
}
};
My solution, as two side hashmap is needed, and also need to count the hash map counts
My java script solution:
function isMatch(pattern, str){
var map = {}; //store the pairs of pattern and strings
function checkMatch(pattern, str) {
if (pattern.length == 0 && str.length == 0){
return true;
}
//if the pattern or the string is empty
if (pattern.length == 0 || str.length == 0){
return false;
}
//store the next pattern
var currentPattern = pattern.charAt(0);
if (currentPattern in map){
//the pattern has alredy seen, check if there is a match with the string
if (str.length >= map[currentPattern].length && str.startsWith(map[currentPattern])){
//there is a match, try all other posibilities
return checkMatch(pattern.substring(1), str.substring(map[currentPattern].length));
} else {
//no match, return false
return false;
}
}
//the current pattern is new, try all the posibilities of current string
for (var i=1; i <= str.length; i++){
var stringToCheck = str.substring(0, i);
//store in the map
map[currentPattern] = stringToCheck;
//try the rest
var match = checkMatch(pattern.substring(1), str.substring(i));
if (match){
//there is a match
return true;
} else {
//if there is no match, delete the pair from the map
delete map[currentPattern];
}
}
return false;
}
return checkMatch(pattern, str);
}
A solution in Java I wrote (based on this HackerRank Dropbox Challenge practice).
You can play with the DEBUG_VARIATIONS and DEBUG_MATCH flags to have a better understanding of how the algorithm works.
It may be too late now, but you might want to attempt to tackle the problem at HackerRank first before reading through the proposed solutions! ;-)
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class Solution {
private static final boolean DEBUG_VARIATIONS = false;
private static final boolean DEBUG_MATCH = true;
static int wordpattern(final String pattern, final String input) {
if (pattern.length() == 1) {
return 1;
}
final int nWords = pattern.length();
final List<List<String>> lists = split(input, nWords);
for (final List<String> words : lists) {
if (DEBUG_VARIATIONS) {
System.out.print("-> ");
for (int i = 0; i < words.size(); i++) {
System.out.printf("%s ", words.get(i));
}
System.out.println();
}
if (matches(pattern, words)) {
return 1;
}
}
return 0;
}
// Return every possible way to split 'input' into 'n' parts
private static final List<List<String>> split(final String input, final int n) {
final List<List<String>> variations = new ArrayList<>();
// Stop recursion when n == 2
if (n == 2) {
for (int i = 1; i < input.length(); i++) {
final List<String> l = new ArrayList<>();
l.add(input.substring(0, i));
l.add(input.substring(i));
variations.add(l);
}
return variations;
}
for (int i = 1; i < input.length() - n + 1; i++) {
final List<List<String>> result = split(input.substring(i), n - 1);
for (List<String> l : result) {
l.add(0, input.substring(0, i));
}
variations.addAll(result);
}
return variations;
}
// Return 'true' if list of words matches patterns
private static final boolean matches(final String pattern, final List<String> words) {
final Map<String, String> patterns = new HashMap<>();
for (int i = 0; i < pattern.length(); i++) {
final String key = String.valueOf(pattern.charAt(i));
final String value = words.get(i);
boolean hasKey = patterns.containsKey(key);
boolean hasValue = patterns.containsValue(value);
if (!hasKey && !hasValue) {
patterns.put(key, value);
} else if (hasKey && !hasValue) {
return false;
} else if (!hasKey && hasValue) {
return false;
} else if (hasKey && hasValue) {
if (!value.equals(patterns.get(key))) {
return false;
}
}
}
if (DEBUG_MATCH) {
System.out.print("Found match! -> ");
for (int i = 0; i < words.size(); i++) {
System.out.printf("%s ", words.get(i));
}
System.out.println();
}
return true;
}
public static void main(final String[] args) {
System.out.println(wordpattern("abba", "redbluebluered"));
}
}
Python solution based on Java solution at: https://www.algo.monster/problems/word_pattern_ii
def helper(pattern, s, idxPattern, idxString, myMap, mySet):
if (idxPattern == len(pattern)) and (idxString == len(s)):
return True
if (idxPattern >= len(pattern)) or (idxString >= len(s)):
return False
thisChar = pattern[idxPattern]
#print ("At Char: ", thisChar, " at location: ", idxPattern)
for idxK in range(idxString + 1, len(s) + 1):
subString = s[idxString:idxK]
if (thisChar not in myMap) and (subString not in mySet) :
myMap[thisChar] = subString
mySet.add(subString)
# print ("Before Map {0}, Set: {1}".format(myMap, mySet))
if helper(pattern, s, idxPattern + 1, idxK, myMap, mySet):
return True
myMap.pop(thisChar)
mySet.remove(subString)
# print ("After Map {0}, Set: {1}".format(myMap, mySet))
elif (thisChar in myMap) and (myMap[thisChar] == subString):
if helper(pattern, s, idxPattern + 1, idxK, myMap, mySet):
return True
def word_pattern_match(pattern: str, s: str) -> bool:
# WRITE YOUR BRILLIANT CODE HERE
print ("Pattern {0}, String {1}".format(pattern, s))
if (len(pattern) == 0) and (len(s) == 0):
return True
if (len(pattern) == 0):
return False
myMap = dict()
mySet = set()
return helper(pattern, s, 0, 0, myMap, mySet)
if __name__ == '__main__':
pattern = input()
s = input()
res = word_pattern_match(pattern, s)
print('true' if res else 'false')
recursively check each combination.
#include <bits/stdc++.h>
using namespace std;
/**
* Given a string and a pattern, check if the whole string is following the given pattern.
* e.g.
* string pattern return
* redblueredblue abab a:red, b:blue true
* redbb aba false
*
* Concept:
* Recursively checking
* point_pat:0 point_str:0 a:r point_pat:1 point_str:1 b:e/ed/edb...
* point_pat:0 point_str:1 a:re point_pat:1 point_str:2 b:d/db/dbl...
*/
bool isMatch(const string &str, const string &pattern, unordered_map<char, string> &match_table, int point_str, int point_pat)
{
if (point_pat >= pattern.size() && point_str >= str.size())
return true;
if (point_pat >= pattern.size() || point_str >= str.size())
return false;
if (match_table.count(pattern[point_pat]))
{
auto &match_str = match_table[pattern[point_pat]];
if (str.substr(point_str, match_str.size()) == match_str)
return isMatch(str, pattern, match_table, point_str + match_str.size(), point_pat + 1);
else
return false;
}
else
{
for (int len = 1; len <= str.size() - point_str; ++len)
{
match_table[pattern[point_pat]] = str.substr(point_str, len);
if (isMatch(str, pattern, match_table, point_str + len, point_pat + 1))
{
return true;
}
}
return false;
}
}
bool isMatch(const string &str, const string &pattern)
{
unordered_map<char, string> match_table;
bool res = isMatch(str, pattern, match_table, 0, 0);
for (const auto &p : match_table)
{
cout << p.first << " : " << p.second << "\n";
}
return res;
}
int main()
{
string str{"redblueredblue"}, pattern{"abab"};
cout << isMatch(str, pattern) << "\n";
cout << isMatch(str, "ab") << "\n";
cout << isMatch(str, "ababa") << "\n";
cout << isMatch(str, "cba") << "\n";
cout << isMatch(str, "abcabc") << "\n";
cout << isMatch("patrpatrr", "aba") << "\n";
}
Depending on what patterns are given, you can answer a 'different' question (that really is the same question).
For patterns like [a b b a] determine whether or not the string is a palindrome.
For patterns like [a b a b] determine if the second half of the string equals the first half of the string.
Longer patterns like [a b c b c a], but you still break it up into smaller problems to solve. For this one, you know that the last n characters of the string should be the reverse of the first n characters. Once they stop being equal, you simply have another [b c b c] problem to check for.
Although possible, in an interview, I doubt they'd give you anything more complex than maybe 3-4 different substrings.