You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

在R语言中优雅实现多词搜索的方法(含进阶匹配需求)

General Solution for N-Word Search with Cross-Letter Constraints

Let's break down how to build a flexible, R-idiomatic solution for your word search game—one that handles any number of words and the advanced cross-letter constraints marked by numbers. We'll use tidyverse tools (dplyr, purrr, stringr) to keep the code clean and scalable.

Step 1: Basic Multi-Word Search (Dynamic for N Words)

The core idea here is to generate filtered word lists for each pattern, then dynamically cross-join all these lists. This avoids hardcoding joins for 2 words and scales to any number of patterns.

Helper Function for Single Pattern Filtering

First, a utility function to filter words matching a single dot-based pattern:

filter_single_pattern <- function(pattern, words_df) {
  len <- nchar(pattern)
  regex <- paste0('\\b', pattern, '\\b')
  words_df %>%
    filter(word_length == len, str_detect(word, regex))
}

Dynamic Cross Join for N Patterns

For any number of patterns, we'll generate filtered lists, rename columns to avoid conflicts, then cross-join them all:

library(tidyverse)

# Sample word dataset (replace with your full 173k-word list)
words <- data.frame(
  word = c('test', 'word', 'active', 'angina', 'endite', 'endive', 'engine', 'entire', 'alanine', 'evening', 'escape', 'entered'),
  word_length = c(4, 4, 6, 6, 6, 6, 6, 6, 7, 7, 7, 7)
)

# Example: Basic 2-word search
find_basic <- str_split('.n.... ...n.n.', ' ')[[1]]

# Generate filtered lists for each pattern (rename columns to word1, word2, etc.)
filtered_list <- map2(find_basic, seq_along(find_basic), function(pat, idx) {
  filter_single_pattern(pat, words) %>%
    rename(!!paste0('word', idx) := word) %>%
    select(-word_length)
})

# Cross-join all filtered lists to get all valid combinations
basic_result <- filtered_list %>%
  reduce(full_join, by = character())

basic_result

Step 2: Advanced Cross-Letter Constraints

To handle number-marked constraints (same number = same letter across positions), we need to:

  1. Convert number patterns to dot patterns for initial filtering.
  2. Parse number positions into constraint groups.
  3. Generate dynamic filter expressions to enforce these constraints.

Helper Functions for Constraint Handling

Let's build tools to parse and apply constraints:

# Convert number patterns to dot patterns for initial filtering
convert_to_dot_pattern <- function(pattern) {
  str_replace_all(pattern, '\\d', '.')
}

# Parse patterns into constraint groups (number → list of (word index, position))
parse_constraints <- function(find_patterns) {
  map_df(seq_along(find_patterns), function(word_idx) {
    chars <- str_split(find_patterns[word_idx], '')[[1]]
    tibble(
      word_idx = word_idx,
      position = which(str_detect(chars, '\\d')),
      number = chars[str_detect(chars, '\\d')]
    )
  }) %>%
    group_by(number) %>%
    nest() %>%
    ungroup()
}

# Generate a filter expression for a single constraint group
create_constraint_filter <- function(constraint_data) {
  if(nrow(constraint_data) < 2) return(NULL) # No constraint needed for single position
  
  # Use the first position in the group as the reference
  first_word <- sym(paste0('word', constraint_data$word_idx[1]))
  first_pos <- constraint_data$position[1]
  first_expr <- expr(str_sub(!!first_word, !!first_pos, !!first_pos))
  
  # Create expressions to compare all other positions to the reference
  other_exprs <- map2(constraint_data$word_idx[-1], constraint_data$position[-1], function(w_idx, pos) {
    current_word <- sym(paste0('word', w_idx))
    current_expr <- expr(str_sub(!!current_word, !!pos, !!pos))
    expr(!!first_expr == !!current_expr)
  })
  
  # Combine all comparisons with logical AND
  reduce(other_exprs, function(a, b) expr(!!a & !!b))
}

Combine all steps into a single function that works for both basic and advanced cases:

search_words <- function(words_df, find_patterns) {
  # Step 1: Initial filtering with dot patterns
  dot_patterns <- map_chr(find_patterns, convert_to_dot_pattern)
  
  filtered_list <- map2(dot_patterns, seq_along(find_patterns), function(pat, idx) {
    filter_single_pattern(pat, words_df) %>%
      rename(!!paste0('word', idx) := word) %>%
      select(-word_length)
  })
  
  # Step 2: Cross-join all filtered word lists
  cross_joined <- filtered_list %>%
    reduce(full_join, by = character())
  
  # Step 3: Apply cross-word constraints if numbers are present
  if(any(str_detect(find_patterns, '\\d'))) {
    constraints <- parse_constraints(find_patterns)
    
    filter_exprs <- constraints$data %>%
      map(create_constraint_filter) %>%
      compact() # Remove groups with only one position (no constraint)
    
    if(length(filter_exprs) > 0) {
      final_filter <- reduce(filter_exprs, function(a, b) expr(!!a & !!b))
      cross_joined <- cross_joined %>% filter(!!final_filter)
    }
  }
  
  # Return only the word columns
  cross_joined %>% select(starts_with('word'))
}

Testing the Advanced Case

Let's test with your example where cross-word constraints are required:

# Advanced 2-word search with constraints
find_advanced <- c('1n2341', '151n3n6')

advanced_result <- search_words(words, find_advanced)
advanced_result

This returns the exact same valid pairs as your manual implementation:

word1   word2
1 angina alanine
2 endite evening
3 endive evening
4 engine evening
5 entire evening

Scaling to N Words

This solution works seamlessly for any number of words. For example, if you have 3 patterns, just pass a vector of 3 patterns to search_words—the function will handle filtering, cross-joining, and applying any cross-word constraints automatically.

Notes for Large Word Lists

  • Initial filtering is critical for your 173k-word list to reduce the number of combinations before cross-joining.
  • If memory becomes an issue, use dplyr::inner_join instead of full_join if you only want valid combinations (no NA values).

内容的提问来源于stack exchange,提问作者Matt_B

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.11 09:03:26