One good technique that I've seen used in the past for information retrieval applications is to shingle your target documents and query document, and then take the Jaccard similarity over sets of shingles.
Shingling is a procedure that takes a sliding window over characters in your document, representing a string as the set of character $k$-grams that occur in the strong. Now that we have a set representation of documents, we can compare them using their Jaccard similarity.
As an example, the 2-shingles of the string "racecar"
would be the set {ra, ac, ce, ec, ca, ar}
.
Below is some simple Python code that would do this to illustrate the point:
def shingle(doc, k=3):
return { doc[i:i+k] for i in range(0, len(doc) - k + 1) }
def jaccard(a, b):
return 1.0 * len(a.intersection(b)) / len(a.union(b))
documents = [...] # List of strings
query = "this week on twitter..."
query_shingles = shingle(query)
best_doc = -1
best_score = float("-inf")
for i, doc in enumerate(documents):
doc_shingles = shingle(doc)
similarity = jaccard(doc_shingles, query_shingles)
if similarity > best_score:
best_score = similarity
best_doc = i
The idea is that documents with similar $k$-shingles have similar contents for a well-chosen value of $k$. For most applications $k$ is almost always between 2 and 4 (3 is the most popular choice that I've seen).
There are alternative formulations such as $w$-shingling which use word $n$-grams instead of character $n$-grams, but the idea is still the same.