有效地计算所有包含所有元音的弦对

时间:2019-03-03 16:14:03

标签: string

我已经解决了以下问题,但仍然需要提高性能:

  

我有N个字。对于每个有效的i,单词i由一个   字符串Di仅包含小写的元音,即字符“ a”,“ e”,   'i','o','u'。

     

(无序)单词对的总数是多少,例如   连接在一起,它们包含所有元音吗?

我的C ++代码使用位数组来表示单词中存在的元音。然后,我通过组合它们的位数组来检查所有字符串对,以查看是否设置了所有元音位。我通过将组合与位数组“ complete”进行比较来做到这一点,该数组具有与元音相对应的所有位:

#include<bits/stdc++.h>
using namespace std;

int main()
{
  int t;
  cin>>t;      // number of different test cases
  while(t--)
  {
    int n;         // number of strings 
    cin>>n;

    int con_s[n];
    for(int i=0;i<n;i++)
    {
      string s;
      cin>>s;
      con_s[i]=0;      // converting vowels present in string into a bit array 
      for(int j=0;j<s.length();j++)
      {
        con_s[i] = con_s[i] | (1<<(s[j]-'a'));
      }
    }
    int complete = 0; // bit array corresponding to all possible vowels 
    complete = (1<<('a'-'a')) | (1<<('e'-'a')) | (1<<('i'-'a')) | (1<<('o'-'a')) | (1<<('u'-'a'));
    // cout<<complete;
    int count = 0;
    for(int i=0;i<n-1;i++)        // check the pairs
    {
      for(int j=i+1;j<n;j++)
      {
        if((con_s[i] | con_s[j])==complete)count++;
      }
    }
    cout<<count<<"\n";
  }
  return 0;
}

尽管位数组使用了非常快的位操作,但是当输入字符串集更大时,我的算法似乎效率不够。谁能建议一个有效的解决方案?

其他信息

测试用例:

Input:
   1
   3
   aaooaoaooa
   uiieieiieieuuu
   aeioooeeiiaiei

Result: 2

说明: 2对(1和2)和(2和3)串联时包含所有5个元音,而对(1和3)不匹配条件,因为串联不包含'u'。因此结果是2。

约束:

1≤T≤1,000
1≤N≤105
1≤|Di|≤1,000 for each valid i
the sum of all |D_i| over all test cases does not exceed 3⋅107

1 个答案:

答案 0 :(得分:0)

假设您有很多字符串。您的算法将比较它们之间的所有字符串,这就是可怕的迭代次数。

重大改进:

新方法

构建一个映射,将一串有序的唯一元音(例如“ ae”)与找到的包含这些唯一元音的所有字符串的列表相关联,无论重复的次数和顺序如何。例如:

ao -> aaooaoaooa, aoa, aaooaaooooooo  (3 words)
uie -> uiieieiieieuuu, uuuuiiiieeee   (2 words)
aeio -> aeioooeeiiaiei                (1 word)

当然,有很多字符串,因此在代码中,您将使用位图而不是有序唯一元音的字符串。还要注意,您不想生成组合字符串的列表,而只想生成它们的计数。因此,您不需要所有字符串事件列表,只需维护与位图匹配的字符串数即可:

16385 -> 3 
1048848 -> 2
16657 -> 1  

然后像您一样查看映射现有索引之间的获胜组合。对于较大的字符串列表,您将具有较小的映射索引列表,因此这将是一项重大改进。

对于每个获胜组合,将第一个字符串列表的大小乘以第二个字符串列表的大小,以增加计数。

16385 1048848 is complete -> 3 x 2 = 6 combinations
1048848 16657 is complete -> 2 x 1 = 2 combinations 
                                    ---
                                     8 potential combinations  

有什么改进?

这些组合是通过分析3x2位图而不是查看对应于唯一字符串的6x5位图找到的。如果您拥有更多的弦,则增益会显着提高。

更笼统地说,由于您有5个窝并且必须至少有一个窝,所以最多只能有2<<5-1个,所以31个不同的位图也就是最多C(31,2),即465 31*30是930 组合,用于检查输入的字符串是100还是1000万。我说它大约为O(n)正确吗?

可能的实现:

map<int, int> mymap; 
int n;
cin>>n;

for(int i=0;i<n;i++) {
    string s;
    cin>>s;
    int bm=0;
    for(int j=0;j<s.length();j++)
        bm |= (1<<(s[j]-'a'));
    mymap[bm]++;
}
int complete = (1<<('a'-'a')) | (1<<('e'-'a')) | (1<<('i'-'a')) | (1<<('o'-'a')) | (1<<('u'-'a'));
int count = 0;
int comparisons = 0; 
for (auto i=mymap.begin(); i!=mymap.end(); i++)  {
    auto j=i; 
    for(++j;j!=mymap.end();j++) {
        comparisons++; 
        if((i->first | j->first)==complete) {
            count += i->second * j->second; 
            cout << i->first <<" "<<j->first<<" :"<<i->second<<" "<<j->second<<endl;
        }
    }
}
auto special = mymap.find(complete);  // special case: all strings having all letters
if (special!=mymap.end()) {           // can be combined with themselves as well
    count += special->second * (special->second -1) / 2; 
}
cout<<"Result: "<<count<<" (found in "<<comparisons<<" comparisons)\n";

Online demo with 4 examples(仅包含所有字母的字符串,您的初始示例,上面的示例以及包含多个其他字符串的示例)