I have the following structure for my object:
Word (Object)
Word
(String)List<Symbol>
Symbol (Object)
Symbol
(String)List<SymbolChoice>
SymbolChoice (Object)
Symbol
(String)
The background of this is that this is processed OCR output and I want to force a word to match a certain regex. If it cannot find a match, it should return an empty List<SymbolChoice>
, otherwise it should return all List<SymbolChoice>
that match the specified regex.
Example:
- Found word:
"1o0"
- Symbols:
"1"
,"o"
and"0"
- Choices of
"1"
:"1"
- Choices of
"o"
:"o"
,"0"
- Choices of
"0"
:"0"
When forcing this word to abide regex "\\d{3}"
, it has to return [["1", "0", "0"]]
, here I am denoting the SymbolChoice
objects as strings.
I have a solution, however it is not fast enough, more details after the method:
public List<List<SymbolChoice>> forceFitRegex(final String regex) {
Objects.requireNonNull(regex, "regex");
if (getSymbols().size() <= 1) {
return new ArrayList<List<SymbolChoice>>();
}
int combinations = 1;
for (Symbol symbol : getSymbols()) {
combinations *= symbol.getSymbolChoices().size();
}
System.out.println("Combinations: " + combinations);
List<List<SymbolChoice>> listOfSymbolChoices = new ArrayList<List<SymbolChoice>>();
for (SymbolChoice symbolChoice : getSymbols().get(0).getSymbolChoices()) {
List<SymbolChoice> symbolChoices = new ArrayList<SymbolChoice>();
symbolChoices.add(symbolChoice);
listOfSymbolChoices.add(symbolChoices);
}
int i = 1;
while (i < getSymbols().size()) {
List<List<SymbolChoice>> listOfSymbolChoicesCopy = new ArrayList<List<SymbolChoice>>(listOfSymbolChoices);
for (SymbolChoice symbolChoice : getSymbols().get(i).getSymbolChoices()) {
for (List<SymbolChoice> symbolChoicesCopy : listOfSymbolChoicesCopy) {
List<SymbolChoice> symbolChoices = new ArrayList<SymbolChoice>(symbolChoicesCopy);
symbolChoices.add(symbolChoice);
listOfSymbolChoices.add(symbolChoices);
}
}
i++;
}
Pattern pattern = Pattern.compile(regex);
ListIterator<List<SymbolChoice>> symbolChoicesListIterator = listOfSymbolChoices.listIterator();
while (symbolChoicesListIterator.hasNext()) {
List<SymbolChoice> symbolChoices = symbolChoicesListIterator.next();
StringBuilder stringBuilder = new StringBuilder();
for (SymbolChoice symbolChoice : symbolChoices) {
stringBuilder.append(symbolChoice.getSymbol());
}
if (!pattern.matcher(stringBuilder.toString()).matches()) {
symbolChoicesListIterator.remove();
}
}
return listOfSymbolChoices;
}
Let n
be the number of combinations possible, calculated as the product of the size of all symbol choices. In my example that would be 1 * 2 * 1 = 2
.
For n <= 288
I observe it to run very quick, however with for example n = 5760
it takes a few minutes to process. I also spot here another case of n = 311040
and have a feeling that it will never finish.
What can I do to solve my problem efficiently? I need to stick to Java 6 for Android compatibility.
(Bonus points if someone can calculate the asymptotic time my algorithm takes)