Reputation:
Imagine my two friends Wendy
and Hunter
name their kid Henry
. Notice that the name Henry
can be created from Hunter
and from Wendy
by merging subsets of the characters of each parent's name (without changing their order). More specifically:
"henry"
is "hnr"
and "ey"
, where the order of characters within each parent's name remains unchanged.
"hnr"
is a subset of the characters in "hunter"
, where the characters remain in order.
We can make a similar observation of "ey"
and "wendy"
.
Question:
Is there a simple way to verify whether any given name can be generated by two parent names without simply generating all the possible child names for a couple?
i.e. Can I easily check isSpecialName("Dan", "Jane", "Adam")
- whether "Dan"
can be created in such a fashion by the names "Jane"
and "Adam"
, without having to check it against all the merged ordered character subsets of "Jane"
and "Adam"
?
Upvotes: 1
Views: 98
Reputation: 3818
Let's say we want to justify if string a
is special for string b
and string c
.
An important observation is, if a
special for b
and c
, then remove the last character of a
, got a'
, it is still special for b
and c
. That is to say:
if isSpecial(a, b, c) is True
then isSpecial(a[0..-1], b, c) is True
This is a sub optimal pattern, so we can use dynamic programming algorithm.
Let f(i, j, k)
represents if a[0..i]
is special for b[0..j]
and c[0..k]
.
a[i] == b[j] => f(i, j, k) sub pattern is f(i-1, j-1, k)
a[i] == c[k] => f(i, j, k) sub pattern is f(i-1, j, k-1)
otherwise => f(i, j, k) sub pattern is f(i, j, k-1) & f(i, j-1, k)
I have written a small c program to verify this algorithm. Code is not as concise as the algorithm though.
Time complexity O(la*lb*lc)
, space complexity O(la*lb*lc)
#include <stdio.h>
#include <string.h>
#include <stdbool.h>
#include <ctype.h>
#define MAX_LEN 10
#define SPECIAL '#'
bool f[MAX_LEN+1][MAX_LEN+1][MAX_LEN+1];
bool isSpecialName(char *pa, char *pb, char *pc) {
int la = strlen(pa);
int lb = strlen(pb);
int lc = strlen(pc);
if (la > lb + lc) return false;
memset(f, false, sizeof(f));
memset(f[0], true, sizeof(f[0]));
for (int i=1; i<=la; ++i) for (int j=0; j<=lb; ++j) for (int k=0; k<=lc; ++k) {
char a = tolower(pa[i-1]);
char b = j > 0 ? tolower(pb[j-1]) : SPECIAL;
char c = k > 0 ? tolower(pc[k-1]) : SPECIAL;
if (j > 0) f[i][j][k] = f[i][j-1][k] || f[i][j][k];
if (k > 0) f[i][j][k] = f[i][j][k-1] || f[i][j][k];
if (a == b) f[i][j][k] = f[i-1][j-1][k] || f[i][j][k];
if (a == c) f[i][j][k] = f[i-1][j][k-1] || f[i][j][k];
}
return f[la][lb][lc];
}
void check(char *a, char *b, char *c) {
if (isSpecialName(a, b, c)) fprintf(stdout, "'%s' *IS* special name of '%s' and '%s'\n", a, b, c);
else fprintf(stderr, "'%s' is *NOT* special of '%s' and '%s'\n", a, b, c);
}
int main() {
check("ab", "a", "b");
check("Dan", "Jane", "Adam");
check("Henry", "Hunter", "Wendy");
check("abcd", "ac", "bd");
check("abcd", "ac", "bb");
return 0;
}
Upvotes: 4
Reputation: 8753
There are only two or less possibilities when you start matching from the start: either take the first occurrence in mother's or in father's name. Later occurrences could also work, but if they do, taking the first occurence has to work too. So we can write a pretty simple algorithm:
function isSpecialName(child, mother, father) {
child = child.toLowerCase();
mother = mother.toLowerCase();
father = father.toLowerCase();
for (let i = 0, x = 0, y = 0; i < child.length; i++) {
let m = mother.indexOf(child[i], x), f = father.indexOf(child[i], y);
if (m < 0 && f < 0)
return false;
if (f < 0)
x = m + 1;
else if (m < 0)
y = f + 1;
else if (isSpecialName(
child.substr(i + 1), mother.substr(m + 1), father.substr(y)))
return true;
else
y = f + 1;
}
return true;
}
Upvotes: 0