How is std :: set slower than std :: map?

I tried to solve this problem from acm.timus.ru , which basically wants me to output the number of different substrings of a given string (maximum length 5000).

The decisions that I intend to present are desperately ineffective and doomed to exceed the limit of the Exceeded Verdict, subject to restrictions. However, the only way that these two solutions differ (at least as far as I see / understand) is to use std::map<long long, bool>while the other uses std::set <long long>(see the beginning of the last cycle of the cycle. Identical, you can check any diff tool). The solution of the card leads to a “time limit exceeding test 3,” while the specified solution leads to a “time limit exceeding test 2,” which means that test 2 is such that the solution to the card runs faster than the given solution. This is so if I choose the Microsoft Visual Studio 2010 compiler. If I choose GCC, both solutions will lead to TLE in test 3.

I do not ask how to solve the problem effectively. What I ask is how can one explain that use std::mapcan be, apparently, more effective than use std::set. I just don’t see the mechanics of this phenomenon and I hope that someone can understand.

Code1 (uses map, TLE 3):

#include <iostream>
#include <map>
#include <string>
#include <vector>

using namespace std;

int main ()
{
   string s;
   cin >> s;
   vector <long long> p;
   p.push_back(1);
   for (int i = 1; i < s.size(); i++)
      p.push_back(31 * p[i - 1]);
   vector <long long> hash_temp;
   hash_temp.push_back((s[0] - 'a' + 1) * p[0]);
   for (int i = 1; i < s.size(); i++)
      hash_temp.push_back((s[i] - 'a' + 1) * p[i] + hash_temp[i - 1]);   
   int n = s.size();   
   int answer = 0;
   for (int i = 1; i <= n; i++)
   {
      map <long long, bool> hash_ans;
      for (int j = 0; j < n - i + 1; j++)
      {
         if (j == 0)
            hash_ans[hash_temp[j + i - 1] * p[n - j - 1]] = true;
         else
            hash_ans[(hash_temp[j + i - 1] - hash_temp[j - 1]) * p[n - j - 1]] = true;
      }
      answer += hash_ans.size();
   }
   cout << answer;
}

Code2 (uses set, TLE 2):

#include <iostream>
#include <string>
#include <vector>
#include <set>

using namespace std;

int main ()
{
   string s;
   cin >> s;
   vector <long long> p;
   p.push_back(1);
   for (int i = 1; i < s.size(); i++)
      p.push_back(31 * p[i - 1]);
   vector <long long> hash_temp;
   hash_temp.push_back((s[0] - 'a' + 1) * p[0]);
   for (int i = 1; i < s.size(); i++)
      hash_temp.push_back((s[i] - 'a' + 1) * p[i] + hash_temp[i - 1]);   
   int n = s.size();   
   int answer = 0;
   for (int i = 1; i <= n; i++)
   {
      set <long long> hash_ans;
      for (int j = 0; j < n - i + 1; j++)
      {
         if (j == 0)
            hash_ans.insert(hash_temp[j + i - 1] * p[n - j - 1]);
         else
            hash_ans.insert((hash_temp[j + i - 1] - hash_temp[j - 1]) * p[n - j - 1]);
      }
      answer += hash_ans.size();
   }
   cout << answer;
}
+5
source share
3 answers

The actual differences that I see (tell me if I missed something) are that in the case of the card you make

hash_ans[key] = true;

and in the given case you

hash_ans.insert(key);

, , . . , , . , ++ , set::insert() map::operator[]() O (log n) , .

, , ? , node string, - a pair<string const, bool>. , RAM , . node, , .

, , :

  • struct data: string {bool b};, , , ​​ . less<string>, .

  • insert()
    , , , ​​. , , , - .


  • , . , , ++ "undefined ", . .


  • , . , , , .

+2

, , . , , , TLE 2 TLE 3, . , , 2 , 3. , , , , .

, , .

+1

It depends on the implementation algorithms used. Typically, sets are implemented using cards only using a key field. In this case, to use the set, in contrast to the card, there will be a very small invoice.

+1
source

All Articles