为什么我们不能一次性可靠地测试回文

时间:2015-05-28 02:06:37

标签: string algorithm memory palindrome automata

我遇到了“回文”的概念。我试着通过阅读维基百科了解

http://en.wikipedia.org/wiki/Palindrome#Computation_theory

该段引起了我的注意

  

这意味着数量有限的计算机是不可能的   记忆能够在一次通过中可靠地测试回文。

我想测试给定的字符串是否是“回文”是非常直接的?我出了一个快速代码。

public class Utils {
    private static final String SPECIAL_CHARACTERS_REGEX = "[\\s!,]";

    private static String removeSpecialCharacters(String string) {
        return string.replaceAll(SPECIAL_CHARACTERS_REGEX, "");
    }

    public static boolean isPalindrome(String string) {
        String str = string.toLowerCase();
        str = removeSpecialCharacters(str);

        if (str.isEmpty()) {
            return false;
        }

        int head = 0;
        int tail = str.length() - 1;
        while (head < tail) {
            char h = str.charAt(head);
            char t = str.charAt(tail);

            if (h != t) {
                return false;
            }

            head++;
            tail--;
        }

        return true;
    }
}

乍一看似乎工作正常。

public static void main(String[] args) {
    String s = "";
    System.out.println(s + " -> " + Utils.isPalindrome(s)); // false

    s = "1";
    System.out.println(s + " -> " + Utils.isPalindrome(s)); // true

    s = "12";
    System.out.println(s + " -> " + Utils.isPalindrome(s)); // false

    s = "123";
    System.out.println(s + " -> " + Utils.isPalindrome(s)); // false

    s = "taco cat";
    System.out.println(s + " -> " + Utils.isPalindrome(s)); // true

    s = "A man, a plan, a canal, Panama!";
    System.out.println(s + " -> " + Utils.isPalindrome(s)); // true

    s = "Amor, Roma";
    System.out.println(s + " -> " + Utils.isPalindrome(s)); // true
}

如果是这样,我可以知道为什么维基百科说一次通过测试回文是不可能的吗?我忽略了什么吗?

3 个答案:

答案 0 :(得分:7)

问题不在于检查已经在内存中的字符串是否是回文。如果你可以把字符串放到内存中,那么检查就可以轻松完成,但是你已经通过将字符串读入内存来使用了一遍,所以检查是第二遍。

但是只有当整个字符串适合内存时才会有效。由于前提是内存是有限的,这意味着你无法验证长度超过内存容量的字符串是否是回文,这就是你引用的句子所说的。

相比之下,你可以在任意长的字符串上使用有限内存进行大量检查。例如,您可以检查字符串的长度是否可被5整除。您可以检查字符串中的每个a是否紧跟b。通常,您可以检查字符串是否与任何正则表达式匹配(这里,我的意思是数学意义上的正则表达式,而不是“正则表达式”库识别的模式。)但是因为您无法使用正则表达式来描述所有回文的集合一个正则表达式,你不能在一次传递中验证一个任意长的字符串是一个回文,只使用有限的内存。

答案 1 :(得分:2)

您刚刚错过了引用行之前的第一行: -

  

在自动机理论中,给定字母表中的一组所有回文   是一种无上下文的语言的典型示例,但不是   规则的。

在这里,他们正在谈论列出给定alphabe的所有可能的回文。

让我们谈谈二进制字母表,A = {0,1}。考虑语言 - &gt;字母A上的回文数量。可以有无限数量的回文字符串,如1,0,11,00,101,111,......等等。

在回文语言的情况下, 至少不可能得到中间元素的概念并将其保存在内存(轨道)中,在同一次传递中,在有限的内存系统中 即可。为此,您需要跟踪正在评估的字符串的各种字符,以及如何确定传入的字符与您访问过的字符相反,只能在有限内存系统中单次传递?

维基百科还指出: -

  

此外,一组回文可能无法通过a进行可靠的测试   确定性下推自动机。从中读取回文时   从左到右,实质上是不可能找到中间的&#34;   直到整个单词被完全阅读。

这种由所有这些字符串组成的语言不能在有限存储器系统中单次评估,因此,由于有限的内存限制,它不能成为常规语言(常规语言可以定义为有限自动机识别的语言--- 这种语言无法在有限记忆系统中被识别,因为有限记忆系统不能有多次传递< /强>)。因此,该语言无法评估所有这些字符串的回文。因此,它显然是有限记忆系统的一个例子。

这个问题又回到了自动机理论的一个着名问题: -

对于语言E,E = {0^i 1^j | i > j}不常规。并且因此它无法在具有有限存储器的机器上得到证明。您需要抽取引理定理来证明给定的语言不规则。然后,你需要在这里放回下推式自动机。但是,这也有其局限性(不要在这里讨论)

此外,下一行显然打算说具有大量内存和涉及多个通行证的最新技术的现代计算机将轻松实现相同的目标---&gt;

  

(对于现代计算机的实际用途,这种限制会   仅适用于令人难以置信的长字母序列。)   //再次当记忆在现代计算机中耗尽时   检查回文。

答案 2 :(得分:0)

如果字符串的长度为n,则代码使用O(log(n))存储(索引headtail需要O(log(n))位)。因此,您的程序需要无限量的内存才能测试任意长的字符串。

将复杂性分析应用于实际代码是困难的,并且通常在理论上听起来不合理:显然,在Java headtail中,每个都是整数和32位长。但复杂性分析处理的是任意大的输入,而不仅仅是实际编程语言所支持的足够大的实用目的,而且差异很难调和(如此处所示)。