一种可能的算法来确定两个字符串是不是是彼此的字谜? [关闭]
Posted
技术标签:
【中文标题】一种可能的算法来确定两个字符串是不是是彼此的字谜? [关闭]【英文标题】:A possible algorithm for determining whether two strings are anagrams of one another? [closed]一种可能的算法来确定两个字符串是否是彼此的字谜? [关闭] 【发布时间】:2013-01-22 05:57:36 【问题描述】:我有这个想法(使用 C 语言)来检查两个由 ASCII 字母组成的字符串是否是彼此的字谜:
检查字符串长度是否相同。
检查两个字符串的所有字符的 ASCII 值之和是否相同。
检查所有字符的 ASCII 值的乘积对于两个字符串是否相同。
我相信如果所有三个都是正确的,那么字符串必须是彼此的字谜。但是,我无法证明。有人可以帮我证明或反驳这行得通吗?
谢谢!
【问题讨论】:
证明:求解两个方程组。它被过度指定了。如果它有解决方案,那么它必须是微不足道的。 这不是那么微不足道-因为参数的数量不是恒定的..如果我证明它适用于 3 个参数,它并没有说它适用于 7 个参数.. 至少我不知道怎么做.. “平凡”在此上下文中表示全零。我不是在劝你。 我看不出它是如何过度指定的,你能说明一下吗? 最好在math.stackexchange.com上提问 【参考方案1】:我编写了一个快速程序来暴力搜索冲突,发现这种方法并非总是有效。字符串 ABFN 和 AAHM 具有相同的 ASCII 和和积,但不是彼此的字谜。它们的 ASCII 和是 279,ASCII 积是 23,423,400。
还有比这更多的冲突。我的程序搜索了所有长度为 4 的字符串,发现了 11,737 个冲突。
作为参考,这里是 C++ 源代码:
#include <iostream>
#include <map>
#include <string>
#include <vector>
using namespace std;
int main()
/* Sparse 2D table where used[sum][prod] is either nothing or is a string
* whose characters sum to "sum" and whose product is "prod".
*/
map<int, map<int, string> > used;
/* List of all usable characters in the string. */
vector<char> usable;
for (char ch = 'A'; ch <= 'Z'; ch++)
usable.push_back(ch);
for (char ch = 'a'; ch <= 'z'; ch++)
usable.push_back(ch);
/* Brute-force search over all possible length-four strings. To avoid
* iterating over anagrams, the search only explores strings whose letters
* are in increasing ASCII order.
*/
for (int a = 0; a < usable.size(); a++)
for (int b = a; b < usable.size(); b++)
for (int c = b; c < usable.size(); c++)
for (int d = c; d < usable.size(); d++)
/* Compute the sum and product. */
int sum = usable[a] + usable[b] + usable[c] + usable[d];
int prod = usable[a] * usable[b] * usable[c] * usable[d];
/* See if we have already seen this. */
if (used.count(sum) &&
used[sum].count(prod))
cout << "Conflict found: " << usable[a] << usable[b] << usable[c] << usable[d] << " conflicts with " << used[sum][prod] << endl;
/* Update the table. */
used[sum][prod] = string() + usable[a] + usable[b] + usable[c] + usable[d];
希望这会有所帮助!
【讨论】:
感谢您的解决方案.. 这看起来像 C++;它看起来肯定不像 C。四个嵌套的 for() 循环对我来说并不性感。 @wildplasser-我很抱歉-我没有注意到它被标记为 C(我只是把它当作一个算法问题)。我也同意最好使用穷举递归或其他技术来做到这一点,但我正在寻找一个简单的反例,并希望这个程序能找到一个。【参考方案2】:你的方法是错误的;我无法解释为什么,因为我不明白,但至少对于基数 3,有不同的集合具有相同的和和乘积:https://math.stackexchange.com/questions/38671/two-sets-of-3-positive-integers-with-equal-sum-and-product
【讨论】:
这真的很酷!然而,这些集合存在的事实并不能立即成为该方法的反例,因为这些集合中可能没有有效的 ASCII 字母数字。 你是对的,很可能存在整数区间,永远不可能选择具有指定属性的两个不同集合,这意味着将编码转移到这样的区间将使OP的方法可行。不过似乎值得怀疑:)【参考方案3】:字母 a-z 和 A-Z 用于索引 26 个素数的数组,这些素数的乘积用作单词的哈希值。相等的产品 相同的字母。
(以下片段中 primes26[] 数组中的哈希值的顺序基于荷兰语中的字母频率,试图模仿预期的产品)
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#define COUNTOF(a) (sizeof (a)/ sizeof (a)[0])
typedef unsigned long long HashVal;
HashVal hashmem (char *str, size_t len);
unsigned char primes26[] =
5,71,79,19,2,83,31,43,11,53,37,23,41,3,13,73,101,17,29,7,59,47,61,97,89,67,
;
struct anahash
struct anahash *next;
unsigned freq;
HashVal hash;
char word[1];
;
struct anahash *hashtab[1024*1024] = NULL,;
struct anahash *new_word(char *str, size_t len);
struct anahash **hash_find(struct anahash *wp);
/*********************************************/
HashVal hashmem (char *str, size_t len)
size_t idx;
HashVal val=1;
if (!len) return 0;
for (idx = 0; idx < len; idx++)
char ch = str[idx];
if (ch >= 'A' && ch <= 'Z' ) val *= primes26[ ch - 'A'];
else if (ch >= 'a' && ch <= 'z' ) val *= primes26[ ch - 'a'];
else continue;
return val;
struct anahash *new_word(char *str, size_t len)
struct anahash *wp;
if (!len) len = strlen(str);
wp = malloc(len + sizeof *wp );
wp->hash = hashmem(str, len);
wp->next = NULL;
wp->freq = 0;
memcpy (wp->word, str, len);
wp->word[len] = 0;
return wp;
struct anahash **hash_find(struct anahash *wp)
unsigned slot;
struct anahash **pp;
slot = wp->hash % COUNTOF(hashtab);
for (pp = &hashtab[slot]; *pp; pp= &(*pp)->next)
if ((*pp)->hash < wp->hash) continue;
if (strcmp( wp->word, (*pp)->word ) > 0) continue;
break;
return pp;
char buff [16*4096];
int main (void)
size_t pos,end;
struct anahash *wp, **pp;
HashVal val;
memset(hashtab, 0, sizeof hashtab);
while (fgets(buff, sizeof buff, stdin))
for (pos=0; pos < sizeof buff && buff[pos]; )
for(end = pos; end < sizeof buff && buff[end]; end++ )
if (buff[end] < 'A' || buff[end] > 'z') break;
if (buff[end] > 'Z' && buff[end] < 'a') break;
if (end > pos)
wp = new_word(buff+pos, end-pos);
if (!wp) pos=end; continue;
pp = hash_find(wp);
if (!*pp) *pp = wp;
else if ((*pp)->hash == wp->hash
&& !strcmp((*pp)->word , wp->word)) free(wp);
else wp->next = *pp; *pp = wp;
(*pp)->freq +=1;
pos = end;
for(end = pos; end < sizeof buff && buff[end]; end++ )
if (buff[end] >= 'A' && buff[end] <= 'Z') break;
if (buff[end] >= 'z' && buff[end] <= 'a') break;
pos = end;
for (pos = 0; pos < COUNTOF(hashtab); pos++)
if (! &hashtab[pos] ) continue;
for (pp = &hashtab[pos]; wp = *pp; pp = &wp->next)
if (val != wp->hash)
fprintf (stdout, "\nSlot:%u:\n", pos );
val = wp->hash;
fprintf (stdout, "\t%llx:%u:%s\n", wp->hash, wp->freq, wp->word);
return 0;
【讨论】:
对于合理大小的字符串,这不会发生整数溢出吗? 当我在六个月前创建它时,我用几个 10 万字对其进行了压力测试,并没有发现溢出的迹象。 (顺便说一句:你总是可以重新测试可能的冲突)在大多数情况下,溢出不会导致冲突(64 位是很多哈希空间!),但会折叠到其他路径无法到达的值。 (省略 2 是可能的,提供更快的折叠但可能更少的碰撞) 再想一想:省略 2 只会产生奇数。也许然后右移一位可以解决这个问题。【参考方案4】:感谢您提出这么好的问题!我没有试图完全反驳你的主张,而是花了一些时间试图找到方法来增强它,使它成为真的。我的感觉是,如果标准差相等,则两者相等。但是我没有进行那么远的测试,而是进行了更简单的测试,并且还没有找到反例。这是我测试过的:
除了你之前提到的条件,
平方和的ASCII平方根必须相等:我使用以下 python 程序。我没有完整的证据,但也许我的回答会有所帮助。不管怎样,看看吧。
from math import sqrt
class Nothing:
def equalString( self, strA, strB ):
prodA, prodB = 1, 1
sumA, sumB = 0, 0
geoA, geoB = 0, 0
for a in strA:
i = ord( a )
prodA *= i
sumA += i
geoA += ( i ** 2 )
geoA = sqrt( geoA )
for b in strB:
i = ord( b )
prodB *= i
sumB += i
geoB += ( i ** 2 )
geoB = sqrt( geoB )
if prodA == prodB and sumA == sumB and geoA == geoB:
return True
else:
return False
def compareStrings( self ):
first, last = ord( 'A' ), ord( 'z' )
for a in range( first, last + 1 ):
for b in range( a, last + 1 ):
for c in range( b, last + 1 ):
for d in range( c, last + 1 ):
strA = chr( a ) + chr( b ) + chr( c ) + chr( d )
strB = chr( d ) + chr( c ) + chr( b ) + chr( a )
if not self.equalString( strA, strB ):
print "%s and %s should be equal.\n" % ( strA, strB )
print "Done"
【讨论】:
我还测试了五个字符串的长度。【参考方案5】:如果您不介意修改字符串,请对每个字符串进行排序并比较两个签名。
【讨论】:
我认为这不能回答问题。虽然这绝对有效,但问题是关于所提出的算法。以上是关于一种可能的算法来确定两个字符串是不是是彼此的字谜? [关闭]的主要内容,如果未能解决你的问题,请参考以下文章