【问题标题】:number of substring which are whose anagrams are palindrome其字谜是回文的子串的数量
【发布时间】:2017-12-04 08:43:52
【问题描述】:

给定一串数字,计算作为任何回文的字谜的子词(一致子序列)的数量。

我在 Python 中的尝试:

def ispalin(s):
    if len(s)%2==0:
        for i in s:
            if s.count(i)%2!=0:
                return False
    else:
        sum =0
        for i in set(s):
            if s.count(i)%2==1:
                sum = sum+1
        if sum == 1:
            return True
        else:
            return False

    return True

def solution(S):
    # write your code in Python 3.6
    count=len(S)
    for i in range(len(S)):
        for j in range(i+1,len(S)):
            if ispalin(S[i:j+1]):
                count=count+1

    return count

i/o 格式

For example, given:

    S = "02002"
the function should return 11. 
these are 11 substrings whose anagrams are palindrome
"0", "2", "0", "0", "2", "00", "020", "200", "002", "2002", "02002"

大字符串超出时间限制。如何优化上述代码?

我敢打赌,存在比这更好的解决方案,这就是证明 [图片][1]

https://i.stack.imgur.com/7x3Jq.png

【问题讨论】:

  • s.count(i) 我会使用collections.Counter 以获得更好的速度。你能添加一些测试输入和输出吗?
  • @Jean-FrançoisFabre 更新了测试用例,顺便说一句,它的复杂度是 o(n)3 我们必须降低时间复杂度
  • @OmG 我敢打赌,o(n) 中存在解决方案,但我现在不确定
  • 在这种情况下,解决方案应该基于某种排列计数。您不能在线性时间内遍历所有子词,因为它们有 O(N^2) 个。
  • @Vovanrock2002 是的,这里的数学很糟糕

标签: python algorithm


【解决方案1】:

这个问题有一个 O(n) 的解决方案。首先要注意的是,一个子串是任何回文的字谜,如果它的包含数字的数量是偶数或最多存在一个奇数。例如“20020”是plaindrome的字谜,因为“2”的数量是偶数,“0”的数量是奇数(最多一个奇数),而“200202”则不行。

所以我们唯一需要保留的是位数的奇偶性而不是它们的总和。我们可以使用一个 10 位的数字来显示所有数字的奇偶校验。每次访问字符串中的一个数字时,从 0 开始,我们可以用 (2^digit) 异或奇偶校验数。按照您的“02002”示例,这里是通过以二进制格式遍历字符串生成的奇偶校验数:

parity_array = {0000000000, 0000000001, 0000000101, 0000000100, 0000000101 0000000001}

现在我们需要在线性时间内计算字谜的数量。遍历 parity_array 我们使用另一个大小为 1024 的数组(我们称之为 memo)来保持我们访问 parity_array 中特定数字的次数。正如我之前提到的,当且仅当其二进制奇偶校验表示中的 1 位的数量最多为 1 时,子字符串是可以的。因此,对于 parity_array 的每个成员,我们需要检查并在 memo 中添加 11 个元素,其中 xor 与当前 parity_array 值相等to: {0 or 1 or 2 or 4 or 8 ... or 1024} 并总结结果。 总复杂度为 O(n)。

编辑: 我为上面解释的内容添加了 C++ 代码。如果需要,我还可以添加 python 代码:

string sample = "02002";
int parity = 0;
vector<int> parity_array;
parity_array.push_back(parity);
for(int i=0; i<sample.size(); ++i){
    parity ^= 1<<(sample[i]-'0');
    parity_array.push_back(parity);
}
int memo[1025] = {0};
int res=0;
for(int i=0;i<parity_array.size();++i){
    for(int j=-1;j<10;++j)
        res += memo[(1<<j)^parity_array[i]];
    memo[parity_array[i]]++;
}
cout<<res<<endl;

【讨论】:

  • 你能在 python 或任何语言中给出你的解决方案
  • @ArgusMalware 我为解决方案添加了 C++ 代码。如果你愿意,我也可以提供 python 代码。
  • @Alireza 您能否更详细地解释第二个 for 循环?
  • 把这个link放在这里,因为它对相同的解决方案有更好的解释。
【解决方案2】:

您可以使用 O(n) 的内存获得二次复杂度。

制作数字计数器的数组[0..9](或使用Python方便的其他数据结构)

遍历字符串,增加当前数字的计数器并将修改后的数组添加到列表中。之后该列表将包含“累积和” - 每个数字到每个索引的计数(例如,第 40 个字符串条目之前的五个 2)

现在要计算第 i 个和第 j 个条目之间的数字,只需减去 C[j][digit] - C[i][digit]

有趣的问题 - 是否存在更好的复杂性解决方案?

【讨论】:

  • 在代码方面,如果我的解释正确,我建议如下:from collections import Counter; C = [Counter(s[:i + 1]) for i in range(len(s))].
  • @MBo 你的努力表示赞赏,但我需要 O(N) 并且它存在
  • @Vovanrock2002 我认为计数器内部将采用相同的复杂性,并且我必须再次迭代计数器以检查字符频率的长度以检查其回文与否
  • @bosey 是的 - 我不是在提出新的东西,只是给出可以实现文字描述的代码。
  • @MBo 我添加了一个 O(n) 解决方案
猜你喜欢
  • 2015-05-30
  • 1970-01-01
  • 2013-07-04
  • 2015-11-11
  • 2017-12-04
  • 1970-01-01
  • 2021-09-16
  • 2013-12-26
  • 2015-01-05
相关资源
最近更新 更多