Skip to content Search Apps Documentation Source Content File Folder Download Copy Actions State String Boolean Number Struct Map Slice Pointer Function Closure Reference Nil Package Type Interface Unknown

Community realm. Read the code first. Community realm, deployed by its author. Read the code before you interact or send coins.

kmpdemo.gno

2.62 Kb · 69 lines
 1// Package kmpdemo is a small gnoweb demo of the Knuth–Morris–Pratt substring
 2// search provided by the [p/moul/x/daily/kmp](/p/nym-moultest123/x/daily/kmp/v0) library:
 3// it shows the failure table and overlapping matches.
 4//
 5// It contains no search logic of its own. Stateless, so Render is
 6// deterministic — which is precisely what the library is for.
 7package kmpdemo
 8
 9import (
10	"strconv"
11	"strings"
12
13	"gno.land/p/nym-moultest123/x/daily/kmp/v0"
14)
15
16// Render renders the demo for gnoweb.
17func Render(path string) string {
18	var b strings.Builder
19	b.WriteString("# Knuth–Morris–Pratt\n\n")
20	b.WriteString("Linear-time substring search, demoing the ")
21	b.WriteString("[`p/moul/x/daily/kmp`](/p/nym-moultest123/x/daily/kmp/v0) library.\n\n")
22
23	const text = "mississippi"
24	const pattern = "issi"
25
26	b.WriteString("## Failure table\n\n")
27	b.WriteString("For each prefix of `" + pattern + "`, the length of the longest proper ")
28	b.WriteString("prefix that is also a suffix. This is what lets the scan slide the ")
29	b.WriteString("pattern without ever rewinding the text.\n\n")
30	b.WriteString("| i | prefix | table |\n|---|---|---|\n")
31	for i, v := range kmp.Table(pattern) {
32		b.WriteString("| " + strconv.Itoa(i) + " | `" + pattern[:i+1] + "` | " + strconv.Itoa(v) + " |\n")
33	}
34
35	b.WriteString("\n## Searching\n\n")
36	b.WriteString("`" + pattern + "` in `" + text + "`:\n\n")
37	b.WriteString("```\n" + text + "\n")
38	hits := kmp.FindAll(text, pattern)
39	for _, at := range hits {
40		b.WriteString(strings.Repeat(" ", at) + strings.Repeat("^", len(pattern)) + "\n")
41	}
42	b.WriteString("```\n\n")
43	b.WriteString("Matches at " + offsets(hits) + " — **overlapping**, and `Count` agrees: ")
44	b.WriteString(strconv.Itoa(kmp.Count(text, pattern)) + ".\n\n")
45
46	b.WriteString("## Overlap is deliberate\n\n")
47	b.WriteString("`FindAll(\"aaaa\", \"aa\")` returns " + offsets(kmp.FindAll("aaaa", "aa")))
48	b.WriteString(", not just the disjoint ones — \"every occurrence\" read honestly. ")
49	b.WriteString("A caller wanting disjoint matches can filter; one wanting overlap ")
50	b.WriteString("could not recover it.\n\n")
51
52	b.WriteString("## Why it belongs on chain\n\n")
53	b.WriteString("The naive scan is O(n·m): `")
54	b.WriteString(strings.Repeat("a", 8) + "b` inside `" + strings.Repeat("a", 16))
55	b.WriteString("b` re-compares everything it already matched. KMP is O(n+m) with no ")
56	b.WriteString("bad case, so a pathological input is not an attack.\n")
57	return b.String()
58}
59
60func offsets(xs []int) string {
61	if len(xs) == 0 {
62		return "_none_"
63	}
64	parts := make([]string, len(xs))
65	for i, x := range xs {
66		parts[i] = "`" + strconv.Itoa(x) + "`"
67	}
68	return strings.Join(parts, ", ")
69}