Ecco un'implementazione ingenua, ma testata e funzionante che utilizza la ricorsione. Penso che sia O (n ^ 3) a causa dei tre casi sull'asterik. C'è un numero decente di casi d'angolo in questo problema.
Dr. Dobbs ha una discreta recensione su questo problema: link
Sebbene uno dei commenti suggerisse l'uso di Boyer-Moore, non sembra normale che Boyer-Moore supporti i caratteri jolly. C'è un post sul blog che aggiunge i caratteri jolly alla ricerca di stringhe KMP - link
from collections import namedtuple
import string
import unittest
LITERAL_CHARS = set(string.ascii_letters + string.digits)
def simple_matcher(pattern, string):
if pattern == "":
if string == "":
return True
else:
return False
# We have a non-empty pattern
else:
if string == "":
# If we only have * in the pattern, we can match the empty string.
if all(p == "*" for p in pattern):
return True
else:
return False
# We have a non-empty pattern and string
else:
if pattern[0] in LITERAL_CHARS:
return (pattern [0] == string [0]
and simple_matcher(pattern[1:], string[1:]))
elif pattern[0] == "?":
return simple_matcher(pattern[1:], string[1:])
elif pattern[0] == "*":
return (
# Match one char with * and keep going
simple_matcher(pattern[0:], string[1:])
or
# Don't use the *
simple_matcher(pattern[1:], string[0:])
or
# Only match one char with *
simple_matcher(pattern[1:], string[1:]))
Result = namedtuple('Result', ['pattern', 'string', 'expected'])
class TestSimpleMatcher(unittest.TestCase):
def test_empty_string(self):
results = [Result("", "", True),
Result("a", "", False),
Result("?", "", False),
Result("*", "", True),
Result("**", "", True),
Result("***", "", True)]
for pattern, string, expected in results:
self.assertEqual(simple_matcher(pattern, string), expected)
def test_empty_pattern(self):
results = [Result("", "", True),
Result("", "a", False),
Result("", "1", False),
Result("", "A", False),
Result("", "aaasdfasdf", False)]
for pattern, string, expected in results:
self.assertEqual(simple_matcher(pattern, string), expected)
def test_literals_against_one_char_string(self):
results = [
Result("a", "a", True),
Result("A", "A", True),
Result("1", "1", True),
Result("2", "a", False),
Result("4", "A", False),
Result("B", "1", False)
]
for pattern, string, expected in results:
self.assertEqual(simple_matcher(pattern, string), expected)
def test_question_marks_against_one_char_string(self):
results = [
Result("?", "a", True),
Result("?", "A", True),
Result("?", "1", True),
Result("??", "a", False),
Result("??", "A", False),
Result("??", "1", False),
]
for pattern, string, expected in results:
self.assertEqual(simple_matcher(pattern, string), expected)
def test_asteriks_against_one_char_string(self):
results = [
Result("*", "a", True),
Result("*", "A", True),
Result("*", "1", True),
Result("**", "a", True),
Result("***", "a", True),
Result("*****", "a", True),
]
for pattern, string, expected in results:
self.assertEqual(simple_matcher(pattern, string), expected)
def test_combinations_against_one_char_string(self):
results = [
Result("*?", "a", True),
Result("*A", "A", True),
Result("*?*", "a", True),
Result("*A*", "A", True),
Result("*?a", "a", False),
Result("*B", "A", False),
Result("*??*", "a", False),
Result("*A*?", "A", False),
]
for pattern, string, expected in results:
self.assertEqual(simple_matcher(pattern, string), expected)
def test_combinations_against_three_char_string(self):
results = [
Result("*?", "aaa", True),
Result("???", "aaa", True),
Result("?*A", "BAA", True),
Result("*E*", "1EF", True),
Result("*", "123", True),
Result("****", "123", True),
Result("*?a*", "abc", False),
Result("*B", "Bod", False),
Result("????", "joe", False),
]
for pattern, string, expected in results:
self.assertEqual(simple_matcher(pattern, string), expected)
if __name__ == '__main__':
unittest.main()