Блог пользователя SleepingThread

Автор SleepingThread, история, 2 года назад, По-английски

Hello everyone!

I was trying to solve this problem using hash struct, the complexity of my solution is O(N) which should pass the testings, but the judgment was giving me TLE, and after hours of trying to fix the problem, I decided to remove struct hash and instead of it, build the hash in the main, and Surprisingly it passed the testings!

this is my hash struct:

struct Hash
{
    int n,Base,Mod,Inv;
    vector <int> Po,iPo,Pre;
    Hash(string &s,int base,int mod)
    {
        Mod=mod;
        Base=base;
        Po.pb(1);
        iPo.pb(1);
        Pre.pb(0);
        Inv=inv(base,Mod);
        for(int i=1; i<=(int)s.size(); i++)
            Add(s[i-1]);
    }
    void Add(char c)
    {
        Po.pb(mul(Base,Po.back(),Mod));
        iPo.pb(mul(Inv,iPo.back(),Mod));
        Pre.pb(sum(Pre.back(),mul(c,Po.back(),Mod),Mod));
    }
    int G(int L,int R)
    {
        R++;
        int g=sub(Pre[R],Pre[L],Mod);
        return mul(iPo[L+1],g,Mod);
    }
};

and this is the first submission which was giving TLE and this the accepted submission

you can compare between them and notice that is exactly no difference except replacing the struct with normal code. so the question is, is using struct time consuming ?

  • Проголосовать: нравится
  • +15
  • Проголосовать: не нравится

»
2 года назад, # |
  Проголосовать: нравится -9 Проголосовать: не нравится

maybe using push_back() in the hash struct is the cause, the vectors will spend extra time when it exceeds its size

  • »
    »
    2 года назад, # ^ |
      Проголосовать: нравится 0 Проголосовать: не нравится

    I tried to replace the vectors with arrays, it's definitely better, the new solution reached test 37 instead of 17, but still not working

»
2 года назад, # |
  Проголосовать: нравится +91 Проголосовать: не нравится

This is probably because of inlining Mod. Compilers can generate much faster code to compute modulo when the Mod a compile time constant than a variable. When you use the struct, if the functions don't get inlined, the compiler might not realize that Mod is a compile time constant, but in your second case, the compiler can realize that Mod is always 1e9+7 and is never changed, so it inlines that value. (The same thing might happen if you used helper functions instead of a struct and passed Mod as an argument.)

The best way to guarantee that Mod is a compile time constant is to either declare+use a global const int Mod, or to make Mod a template int parameter to your hash struct.

  • »
    »
    2 года назад, # ^ |
      Проголосовать: нравится +8 Проголосовать: не нравится

    Thank you so much! very important point to know

  • »
    »
    2 года назад, # ^ |
      Проголосовать: нравится +1 Проголосовать: не нравится

    constexpr int Mod

    The compiler can actually detect that a variable's hardcoded and declare it a constexpr (or store a constexpr-declared variable in RAM if you force it) since it's just a hint that automatically gives the const type trait. In this case, the problem's that the code's written specifically to handle a general modulo.

    Do you know what x86 assembly results from inlined modulo (that isn't just a power of 2) vs non-inlined?

    • »
      »
      »
      2 года назад, # ^ |
        Проголосовать: нравится +7 Проголосовать: не нравится

      It's not really accurate to say the "compiler will declare a variable constexpr", since constexpr is more of a language thing and constant folding is more of an optimizer thing, which is lower level. It's worth noting that in C++, constexpr does not guarantee that the compiler will evaluate your expression at compiletime; it's more of a typecheck that it can be evaluated at compiletime. For actual guarantees, see the new consteval and constinit keywords.

      Here's one example of what gcc expands the modulo to: https://godbolt.org/z/Mc8MPxPMa. If you want to look up more literature, I think these transformations are usually called "strength reduction" because you reduce the strength of the general operator a % b to a % (1e9+7).

      • »
        »
        »
        »
        2 года назад, # ^ |
          Проголосовать: нравится 0 Проголосовать: не нравится

        Yeah I mean that getting constants optimised happens very reliably (unlike some other optimisations) so the overall effect is much like writing constexpr. At least for stuff with static linkage.

»
2 года назад, # |
  Проголосовать: нравится 0 Проголосовать: не нравится

I even got TLE without using struct , i think time limit is bad in this problem, it should be 2s .

  • »
    »
    2 года назад, # ^ |
    Rev. 2   Проголосовать: нравится 0 Проголосовать: не нравится

    I think it's not a bad problem, actually, it's a very good one. this kind of problems will keep you trying your best to make your code as fast as possible!

    you are getting TLE for a reason definitely, so keep trying ^_^

  • »
    »
    2 года назад, # ^ |
      Проголосовать: нравится +1 Проголосовать: не нравится

    I added small changes to your solution and got Accepted. 147993909

»
2 года назад, # |
Rev. 2   Проголосовать: нравится +1 Проголосовать: не нравится

Btw, I'm surprised that nobody mentioned that you should use rolling hashes. That way you need 2 arrays instead of 3.

To be more precise you can change your current code to use 2 arrays for building but doing it with rolling hashes is easier.