生成所有变体的算法

Posted

技术标签:

【中文标题】生成所有变体的算法【英文标题】:Algorithm to generate all variants 【发布时间】:2021-08-01 18:01:09 【问题描述】:

我有一点问题,例如一个词treasure

我试图找出这个单词的所有可能变体,只包括字母和数字a-z0-9,它们看起来仍然与原始单词相似。

例如,7r345ur37rea5ure 是几个例子。我需要所有可能的结果我花了很长时间寻找术语的定义,我找到了术语Leet,它指的是替换字母和数字" L33t 5p34k "

我在网上找到的生成器擅长给我 1 个结果,但我需要数千个。

我找到了这篇文章; Algorithm to generate all variants of a word

在其中一个答案中给出了这个 JAVA 代码示例;

static Map<Character, char[]> variants = new HashMap<Character, char[]>() 
    put('a', new char[] 'ä', 'à');
    put('b', new char[]         );
    put('c', new char[]  'ç'    );
; 

public static Set<String> variation(String str) 

    Set<String> result = new HashSet<String>();

    if (str.isEmpty()) 
        result.add("");
        return result;
    

    char c = str.charAt(0);
    for (String tailVariant : variation(str.substring(1))) 
        result.add(c + tailVariant);
        for (char variant : variants.get(c))
            result.add(variant + tailVariant);
    

    return result;

这个代码示例能实现我想要完成的目标吗?

我从未接触过 JAVA,但从外观上我可以添加和编辑这些行;

put('a', new char[] 'ä', 'à');
put('b', new char[]         );
put('c', new char[]  'ç'    );

包含我想使用的所有可能的字符,但有人会善意地更改它,以便将其应用于 Treasure 的输入词。

我真的希望我想要达到的目标在问题中是明确的。

【问题讨论】:

如果您以前从未使用过 Java 编程,那么最好从一些更简单的概念开始,并遵循一些 Java 初学者教程。本网站并非真正为您编写代码,而是帮助解决您的代码方面遇到的具体问题。 目的是什么?您可以使用每个字母替换(置换器)来置换不同的可能性,或者您也可以倒退并将替换转换为英文字母以查找匹配的单词(过滤器)。 【参考方案1】:

你找到的方法就是所谓的递归方法,就是不断调用自身,直到满足某个条件的方法。如果您想简单地对其进行调整并将其用于您的目的,只需更改映射,以便为每个字母分配相应的数字(如果存在可以用作字母替代的数字)。否则将数组留空。即

put('a',new char[] '4');
put('b',new char[] '8');
put('c',new char[] );
put('d',new char[] );
put('e',new char[] '3');
put('f',new char[] );
put('g',new char[] '6');
put('h',new char[] '4');
put('i',new char[] '1');
put('j',new char[] );
put('k',new char[] );
put('l',new char[] '1'");
....
put('y',new char[] '9');
put('z',new char[] '2');

这应该会给你想要的结果。但是,如果您还了解该方法内部发生的事情,那就更好了。递归方法可能很难阅读,而且乍一看并不明显。您可以尝试迭代地实现该方法。

如果您是 java 新手并且无法实现自己创建所有组合的算法,您可能需要使用库。例如,Generex 非常适合您的任务。 Generex 采用正则表达式,可以返回与表达式匹配的随机字符串或与正则表达式匹配的所有可能字符串。一个简单的例子来展示这个库的使用:

import com.mifmif.common.regex.Generex;

public class Example 

    public static void main(String[] args) 
        Generex gen = new Generex("[t7][r2][e3][a4][s5][u][r2][e3]");
        gen.getAllMatchedStrings().forEach(System.out::println);
    

输出:

72345u23
72345u2e
72345ur3
72345ure
7234su23
7234su2e
7234sur3
7234sure
723a5u23
....
trea5ure
treasu23
treasu2e
treasur3
treasure

在上面的示例中,generex 将通过从每个字符类(即从每个方括号中)获取一个字母或数字来创建字符串,并返回所有可能组合的列表。

您可以将上面的内容概括为使用任何输入而不是固定字符串,例如上面示例中的treasure。为此,您需要创建一个映射,其中每个字母作为键,字母和可能的替换数字作为值。之后,您只需在每个字符处拆分输入,从地图中获取每个字母的值并将其传递给generex。下面的内容应该可以帮助您入门。

import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.regex.Pattern;
import java.util.stream.Collectors;

import com.mifmif.common.regex.Generex;

public class Example 
    private static final Map<String, String> MY_MAP = createMap();

    public static void main(String[] args) 
        String myString = "Lewis";

        String random = randomVariant(myString);
        System.out.println(random);
        System.out.println();

        List<String> variants = allVariants(myString);
        variants.forEach(System.out::println);
    

    private static Map<String, String> createMap() 
        Map<String, String> result = new HashMap<>();
        result.put("a","[a4]");
        result.put("b","[b8]");
        result.put("c","[c]");
        result.put("d","[d]");
        result.put("e","[e3]");
        result.put("f","[f]");
        result.put("g","[g6]");
        result.put("h","[h4]");
        result.put("i","[i1]");
        result.put("j","[j]");
        result.put("k","[k]");
        result.put("l","[l1]");
        result.put("m","[m]");
        result.put("n","[n2]");
        result.put("o","[o0]");
        result.put("p","[p]");
        result.put("q","[q]");
        result.put("r","[r2]");
        result.put("s","[s5]");
        result.put("t","[t7]");
        result.put("u","[u]");
        result.put("v","[v]");
        result.put("w","[w]");
        result.put("x","[x]");
        result.put("y","[y9]");
        result.put("z","[z2]");

        return Collections.unmodifiableMap(result);
    

    public static List<String> allVariants(String str)
        String reg = Pattern.compile("").splitAsStream(str.toLowerCase()).map(MY_MAP::get).collect(Collectors.joining());
        Generex gen = new Generex(reg);
        return gen.getAllMatchedStrings();
    

    public static String randomVariant(String str)
        String reg = Pattern.compile("").splitAsStream(str.toLowerCase()).map(MY_MAP::get).collect(Collectors.joining());
        Generex gen = new Generex(reg);
        return gen.random();
    

输出:

13wi5

13w15
13w1s
13wi5
13wis
1ew15
1ew1s
1ewi5
1ewis
l3w15
l3w1s
l3wi5
l3wis
lew15
lew1s
lewi5
lewis

【讨论】:

谢谢你,我真的很感谢你的详细回答,太棒了!你真的帮助了我我非常感激你能告诉我当我运行你的代码时我得到这个package com.mifmif.common.regex does not exist import com.mifmif.common.regex.Generex;这是因为我没有安装Generex吗?我需要单独安装吗,对于这将是我运行的第一个 JAVA 应用程序的问题,我深表歉意。我已经安装了 JDK 并将您的代码保存为 example.java 然后我转到命令行并运行 javac example.java 这是错误时 您使用的是 Eclipse、intelij 或 netbeans 之类的 IDE 吗? 我会下载 intelij 使用 IDE 可以让很多事情变得更容易,比如不必自己编译所有内容,而不必手动包含所需的库。 这不是绝对必要但非常方便,您可以完全专注于编码并在编写而不是编译时遇到可能的错误。安装 Intelij 后,创建一个 Maven 项目,去Maven 复制依赖到你的 pom.xml。如果您不知道 maven 是什么,只需创建一个简单的 java 项目,从同一链接下载 jar 并将其添加到您的类路径【参考方案2】:

试试这个。

static final String[] VARIANTS = "t7","r2","e3","a4","s5","u";
static final Map<Character, String> VARIANTS_MAP = Arrays.stream(VARIANTS)
        .collect(Collectors.toMap(s -> s.charAt(0), Function.identity()));

static Set<String> variation(String word) 
    int size = word.length();
    String[] array = word.chars()
        .mapToObj(c -> VARIANTS_MAP.get((char)c))
        .toArray(String[]::new);
    Set<String> result = new LinkedHashSet<>();
    char[] buffer = new char[size];
    new Object() 
        void gen(int index) 
            if (index >= size) 
                result.add(new String(buffer));
             else 
                String s = array[index];
                for (int i = 0, max = s.length(); i < max; ++i) 
                    buffer[index] = s.charAt(i);
                    gen(index + 1);
                
            
        
    .gen(0);
    return result;


public static void main(String[] args) 
    Set<String> result = variation("treasure");
    System.out.println("size=" + result.size());
    result.forEach(System.out::println);

输出:

size=128
treasure
treasur3
treasu2e
treasu23
 .....
72345u2e
72345u23

【讨论】:

以上是关于生成所有变体的算法的主要内容,如果未能解决你的问题,请参考以下文章

反向传播算法和生成对抗网络的区别

VS生成的连接字符串不起作用,以及它的任何变体

算法划分——二叉树的多种变体

如何获取具有特定名称/值的所有变体ID

隐藏产品可变价格,直到在 WooCommerce 中选择所有变体字段

使用布鲁克林主题在 Shopify 的收藏页面上显示所有颜色变体