[object Object]

← back to Microsite Engine

fix idea site-name segmentation: DP coverage-max splitter + domain-word vocab

d37b80259ab967b011c8a922eda473821b498cc2 · 2026-07-17 10:21:20 -0700 · Steve Abrams

- greedy matcher let the plural variant swallow the next word
  (hospitalitysalesreps → 'Hospitalitys Alesreps'); replaced with a DP that
  maximizes recognized-character coverage (tie: fewer words), so
  hospitality+sales+reps wins over hospitalitys+garbage
- unmatched runs survive as their own word (goldleaf → 'Gold Leaf Wallpaper')
- filler vocab learns common domain words (sales/reps/agents/brokers/news/…)
  and a DISPLAY_FIX maps compound tokens (losangeles → 'Los Angeles')

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

Files touched

Diff

commit d37b80259ab967b011c8a922eda473821b498cc2
Author: Steve Abrams <steve@designerwallcoverings.com>
Date:   Fri Jul 17 10:21:20 2026 -0700

    fix idea site-name segmentation: DP coverage-max splitter + domain-word vocab
    
    - greedy matcher let the plural variant swallow the next word
      (hospitalitysalesreps → 'Hospitalitys Alesreps'); replaced with a DP that
      maximizes recognized-character coverage (tie: fewer words), so
      hospitality+sales+reps wins over hospitalitys+garbage
    - unmatched runs survive as their own word (goldleaf → 'Gold Leaf Wallpaper')
    - filler vocab learns common domain words (sales/reps/agents/brokers/news/…)
      and a DISPLAY_FIX maps compound tokens (losangeles → 'Los Angeles')
    
    Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
---
 server.js | 37 +++++++++++++++++++++++++++++--------
 1 file changed, 29 insertions(+), 8 deletions(-)

diff --git a/server.js b/server.js
index 440c407..ba33bdd 100644
--- a/server.js
+++ b/server.js
@@ -75,16 +75,37 @@ function ownedDomains() {
 }
 // greedy longest-match segmentation of a domain base ("corkwallcovering" →
 // ["cork","wallcovering"]) against the catalog vocabulary + trade filler words
-const FILLER_WORDS = ['wallcoverings', 'wallcovering', 'wallpapers', 'wallpaper', 'designer', 'walls', 'wall', 'the', 'my', 'shop', 'and'];
+const FILLER_WORDS = ['wallcoverings', 'wallcovering', 'wallpapers', 'wallpaper', 'designer', 'design',
+  'walls', 'wall', 'the', 'shop', 'store', 'and', 'sales', 'reps', 'representatives', 'agents',
+  'brokers', 'domain', 'domains', 'news', 'live', 'demand', 'online', 'home', 'decor', 'group', 'losangeles'];
+// compound words that segment as one token but display as several
+const DISPLAY_FIX = { losangeles: 'Los Angeles' };
+// DP segmentation: maximize recognized-character coverage (tie: fewer words), so
+// "hospitalitysalesreps" prefers hospitality+sales+reps over the plural variant
+// "hospitalitys" + garbage. Unmatched runs survive as their own word.
 function segmentBase(base, vocab) {
+  const n = base.length;
+  const best = new Array(n + 1);
+  best[n] = { cov: 0, cnt: 0, next: n, word: null };
+  for (let i = n - 1; i >= 0; i--) {
+    let b = { cov: best[i + 1].cov, cnt: best[i + 1].cnt + 1, next: i + 1, word: null };
+    for (const w of vocab) {
+      if (!base.startsWith(w, i)) continue;
+      const nxt = best[i + w.length];
+      const cov = nxt.cov + w.length, cnt = nxt.cnt + 1;
+      if (cov > b.cov || (cov === b.cov && cnt < b.cnt)) b = { cov, cnt, next: i + w.length, word: w };
+    }
+    best[i] = b;
+  }
   const words = [];
-  let rest = base;
-  while (rest.length) {
-    const hit = vocab.find(w => rest.startsWith(w));
-    if (!hit) { words.push(rest); break; }
-    words.push(hit);
-    rest = rest.slice(hit.length);
+  let i = 0, unk = '';
+  while (i < n) {
+    const s = best[i];
+    if (s.word) { if (unk) { words.push(unk); unk = ''; } words.push(s.word); }
+    else unk += base[i];
+    i = s.next;
   }
+  if (unk) words.push(unk);
   return words;
 }
 function domainIdeas() {
@@ -110,7 +131,7 @@ function domainIdeas() {
     ideas.push({
       domain: dom,
       slug: base,
-      siteName: words.map(w => w[0].toUpperCase() + w.slice(1)).join(' '),
+      siteName: words.map(w => DISPLAY_FIX[w] || (w[0].toUpperCase() + w.slice(1))).join(' '),
       keywords: tags.map(t => t.name),
       count: Math.max(...tags.map(t => t.n)),
     });

← 3071de6 prefilled dropdowns for slug + domain + site ideas (owned CN  ·  back to Microsite Engine  ·  ideas dropdown now carries ALL 267 owned domains in three ti 0c12ece →