Cod sursa(job #955238)

Utilizator mgntMarius B mgnt Data 31 mai 2013 10:18:19
Problema Abc2 Scor 0
Compilator cpp Status done
Runda Arhiva de probleme Marime 18.7 kb

// http://infoarena.ro/problema/abc2
// 
// Folosesc abordarea de aici:
// http://www.infoarena.ro/job_detail/98260?action=view-source
// 
// awesome

#include <cstddef>
#include <cstdio>
#include <vector>

namespace abc2
{

size_t  const thew  = 3; // the w constant
size_t  const thew1 = (1+thew);
size_t  const maxn  = 50000;
size_t  const maxc  = 20;
size_t  const maxc1 = 1+maxc;
size_t  const maxc2 = 2+maxc;
size_t  const maxv  = (maxn*maxc);
size_t  const maxv2 = (2+maxv);
size_t  const maxm  = 10000000;
size_t  const maxm1 = (1+maxm);
size_t  const maxm2 = (2+maxm);

size_t  const HMAX  = 666013;

typedef  ::std::size_t  abcf_node[thew1];
typedef  ::std::size_t  bft_queue[maxv2];


/// \brief  All the data used to solve the problem.
///         The input and extra space used for computing the output.
struct  abc2_data
{ 
  /// \brief  The trie.
  /// 
  /// m_nodes[x][i]    == y ↔ (x) --['a'+i]-->(y), i=0..thew-1
  /// m_nodes[x][thew]  ≠ 0 ↔ x is a leaf in the trie
  ::std::size_t  m_trie[maxv2][thew1];
  
  /// \brief  The dfa.
  /// 
  /// m_nodes[x][i]    == y ↔ (x) --['a'+i]-->(y), i=0..thew-1
  /// m_nodes[x][thew]  ≠ 0 ↔ x is a final state in the dfa
  ::std::size_t  m_dfa[maxv2][thew1];
  
  /// \brief  The queue used in the breadth-first-traversal
  ///         of states of the dfa.
  ::std::size_t  m_bft[maxv2];
  
  /// \brief  Number of occurrences;
  ::std::size_t  m_number_of_occurrences;
  
  /// \brief  Space to store all the words in the Makako language
  ///         , one word after the other, with no separator.
  char  m_words_symbols[maxv2];
  ///        The number of words in the Makako language.
  ::std::size_t  m_number_of_words;
  ///        The length of each word in the Makako language.
  ::std::size_t  m_word_size;
  
  /// \brief  Space to store the ancient text.
  char  m_text_symbols[maxm2];
  /// \brief  The length of the ancient text.
  size_t  m_text_size;
  
  // \brief  This one is used only in extreme cases.
  // Default constructor of ::std::vector should not throw c++ exceptions.
  ::std::vector<size_t>  m_hash_table[HMAX];
  
} // struct abc2_state
;

// ______________________________________________________________________________

/// \brief  Constructs the trie, from the words.
class trie_build_processing
{ private:
  
  abc2_data  & m_data;
  
  public:
  
  trie_build_processing
  ( abc2_data  & data
  )
  throw()
  : m_data(data)
  { this->build_trie();
  }
  
  private:
  
  void
  build_trie
  ()
  throw()
  ;
  
} // class trie_build_processing
;

void
trie_build_processing::build_trie
()
throw()
{ //  Interation for all words:
  ::std::size_t           s = 0; // The number of nodes in the trie.
  ::std::size_t           i;
  abc2_data             & d    = this->m_data;
  abcf_node             * trie = d.m_trie;
  ::std::size_t     const z    = d.m_number_of_words;
  ::std::size_t     const n    = d.m_word_size;
  char                  * p    = d.m_words_symbols; // the current word
  // Iteration for a word:
  ::std::size_t           x; // position in the trie
  ::std::size_t           j; // position in the current word
  ::std::size_t           c; // current symbol in the current word
  ::std::size_t           y; // next position in the trie
  // Iteration for symbols.
  ::std::size_t           t;
  
  for(t=0; thew > t; ++t)
  { trie[s][t] = 0; }
  trie[s][thew] = 0; // not a leaf
  ++s;
  
  for(i=0; z>i; ++i)
  { // Add current word, into the trie.
    x = 0;
    for(j=0; n>j; ++j)
    { c  = p[j]; // c is 0, 1, or 2.
      y = trie[x][c];
      if(0 == y)
      { for(t=0; thew > t; ++t)
        { trie[s][t] = 0; }
        trie[s][thew] = 0; // not a leaf
        trie[x][c] = s; y = s; ++s;
      }
      
      x = y;
    }
    
    trie[x][thew] = 1; // a leaf
    
    // Update position of the current word.
    p += n;
  }
  
}

// ______________________________________________________________________________



// ###############################################################################
// ###############################################################################
// ###############################################################################




// Ce proprietăți are automatul finit determinist, cu care vom determina
// pozițiile în T, unde se regăsesc șiruri de simboluri din P?
// 
// _______________________________________________________________________________
// Σ este o mulțime finită, oarecare.
// Σ* - șirurilor finite peste Σ.
// ε  - șirul fără nici o componentă.
// ε ∈ Σ* .    Σ+ = Σ* ∖ {ε}.
// 
// P ⊂ Σ+ este o mulțime finită.
// ( P e mulțimea șirurilor pe care le căutăm în șirul T. )
// 
// x ∈ Σ+, y ∈ Σ+, i ∈ ℤ, S ⊂ Σ+, S mulțime finită
// λ(x)   : lungimea șirului x.
// α(x,i) : prefixul de lungime i, al lui șirului x, atunci când 0 ≤ i ≤ λ(x)
// ω(x,i) : sufixul de lungime i, al șirului x, atunci când 0 ≤ i ≤ λ(x)
// ω(x,0) = ε
// x ⊏ y  : (  ( λ(y) > λ(x) ) ∧ ( x = α(y,λ(x)) )  )
// x ⊐ y  : (  ( λ(y) > λ(x) ) ∧ ( x = ω(y,λ(x)) )  )
// σ(y) = { x ∈ Σ+ | x ⊐ y }
// π(S) = { x ∈ Σ+ | ∃ y ∈ S (x ⊏ y) }
// 
// β(x) = ω(x,max { 0 } ∪ { i ∈ ℤ | 1≤ i ≤ λ(x) ∧ ω(x,i) ∈ π(P) }), ∀ x ∈ Σ+
// _______________________________________________________________________________
// 
// Fie D = (Q,Σ,δ,q₀,F) automatul finit construit de acest program.
// 
// γ : Σ* → Q, astfel încât γ(ε)  = q₀, și ∀ σ ∈ Σ* ∀ c ∈ Σ  γ(σc) = δ(γ(σ),c)
// τ : Σ* → ℘(Q), astfel încât τ(ε) = {q₀}, și ∀ σ∈Σ* ∀c∈Σ τ(σc) = τ(σ) ∪ {γ(σc) }
// 
// D are următoarele proprietăți:
//  
//  ▪ q₀ = 0
//    
//  ▪ ∀ q ∈ Q ( ( q₀ = q ) ∨ ( ∃ x ∈ π(P) ∪ P (q = γ(x)) )  )
//    
//  ▪ ∀ x ∈ P ( τ(x) = 1 + λ(x) )
//    
//  ▪ ∀ x ∈ π(P) ∀ c ∈ Σ ( ((xc ∉ π(P) ) ∧ (xc ∉ P)) → (δ(γ(x),c) = γ(β(xc)) )  )
// 
// _______________________________________________________________________________
// Care sunt proprietățiile funcției β?
// 
// Pentru x ∈ π(P) ∪ P, c ∈ Σ, următoarele sunt proprietăți ale funcției β:
//    
//  ▪ β(x)c ∈ π(P) → β(xc) = β(x)c
//    
//  ▪ β(x)c ∉ π(P) → β(xc) = ε



// ###############################################################################
// ###############################################################################
// ###############################################################################



// _______________________________________________________________________________
// The breadth first traversal is needed to compute β, because the breadth
// first traversal explores nodes x, before node y, if the path from the root
// to node x, has fewer edges than the path from the root, to node y.
// 
// Consider the example:
//    P = { cccccccbaaaaaa
//        , aaaaaaaaaaaaaa
//        }
// What is the function β, for P.
//  β(cccccccbaa) = aa
//  It is not possible to derive aa, from cccccccbaa ∈ P alone.
//  The value aa is derived from aaaaaaaaaaaaaa ∈ P.
//  
// This is what the breadh first traversal does:    it prepares values
// needed for computing β, using the recurrence relation.


/// \brief  Constructs the dfa, from the trie.
class dfa_build_processing
{ private:
  
  abc2_data  & m_data;
  
  public:
  
  dfa_build_processing
  ( abc2_data  & data
  )
  throw()
  : m_data(data)
  { this->build_dfa();
  }
  
  private:
  
  void
  build_dfa
  ()
  throw()
  ;
  
} // class dfa_build_processing
;

void
dfa_build_processing::build_dfa // method
()
throw()
{ abc2_data      & d    = this->m_data; // data / context
  abcf_node      * trie = d.m_trie;     // the trie
  abcf_node      * dfa  = d.m_dfa;      // the dfa
  bft_queue      & qa   = d.m_bft;      // queue array
  ::std::size_t    qs   = 0;            // queue size
  ::std::size_t    qi   = 0;            // queue index
  ::std::size_t    x;                   // current state
  ::std::size_t    i;                   // trie: [ x -- [i] --> y ]
  ::std::size_t    y;                   // trie: [ x -- [i] --> y ]
  ::std::size_t    t;                   // x=γ(w), t = γ(β(w·i)), for some w ∈ Σ*
  ::std::size_t    j;                   //  dfa: [ y --[j] --> z  ]
  ::std::size_t    z;                   //  dfa: [ y --[j] --> z  ]
  
  for(i=0; thew>i; ++i)
  { dfa[0][i] = 0; }
  
  qa[0] = 0; qi = 0; qs = 1;
  while(qs > qi) // while
  { x = qa[qi]; ++qi; // Extract state from the bft queue.
    
    dfa[x][thew] = trie[x][thew]; // ≠ 0: final state
    
    for(i=0; thew>i; ++i) // for-1
    { y = trie[x][i];
      
      if(0 != y) // if-1
      { qa[qs] = y; ++qs;
        
        t = dfa[x][i]; // Read t, from dfa[x][i].
        dfa[x][i] = y; // Write final value in dfa[x][i].
        
        for(j=0; thew>j; ++j) // for-2
        { z = trie[t][j];
          if(0 == z)
          { z = dfa[t][j]; }
          
          dfa[y][j] = z; // "Write t", in dfa[y][j].
          
        } // for-2
        
      } // if-1
      
    } // for-1
    
  } // while
  
} // method


// ______________________________________________________________________________

/// \brief  Solves the abc2 problem, using the DFA.
class solver
{ private:
  
  abc2_data  & m_data;
  
  public:
  
  solver
  ( abc2_data  & data
  )
  : m_data(data)
  { this->process_words();
    this->count_number_of_occurrences();
  }
  
  private:
  
  void
  process_words
  ()
  ;
  
  void
  count_number_of_occurrences
  ()
  throw()
  ;
} // class solver
;

void
solver::process_words
()
{ // A two pass algorithm for building the dfa used for
  // the multiple pattern matching problem.
  
  abc2_data  & d = this->m_data;
  
  // Construct the trie, from words.
  trie_build_processing  trie_build_processing_object(d);
         // The constructor needs to be evaluated on trie_build_processing_object
         // because its evaluation contains side effects: it changes the state of
         // the m_data object.
  (void) trie_build_processing_object;
  // Construct the dfa, from trie.
  dfa_build_processing  dfa_build_processing_object(d);
         // Same reasoning about side-effects assures that
         // the constructor needs to be evaluated on dfa_build_processing_object.
  (void) dfa_build_processing_object;
}

void
solver::count_number_of_occurrences
()
throw()
{ // Count occurrences, using the dfa.
  abc2_data               & d        = m_data;
  char                    * q        = d.m_text_symbols;
  size_t                    m        = d.m_text_size;
  size_t                    i;         
  size_t                    c;         
  abcf_node               * dfa      = d.m_dfa;
  size_t                    x        = 0;
  size_t                    y;         
  size_t                    num      = 0; // The number of occurrences.
  bool                      match;     
  
  for(i=0 ; m>i; ++i)
  { c = q[i]; // one of 0,1,...,thew-1.
    y = dfa[x][c];
    match = (0 != (dfa[y][thew]) );
    if(match)
    { ++num; }
    x = y;
  }
  
  // no-throw, no-fail
  ::std::size_t  & ans = d.m_number_of_occurrences;
  ans = num;
}

// ______________________________________________________________________________

/// \brief  Solves with a hash ( Rabin–Karp algorithm )
namespace yet_another_solver
{


/// \brief  Adds words, to hash table.
class words_processing
{ private:
   
  abc2_data  & m_data;
   
  public:
   
  words_processing
  ( abc2_data  & data
  )
  throw()
  : m_data(data)
  { build();
  }
   
  private:
   
  void
  build
  ()
  { this->initialize_with_no_words();
    this->add_words();
  }
   
  void
  initialize_with_no_words
  ()
  ;
   
  void
  add_words
  ()
  ;
   
} // class words_processing
;
 
void
words_processing::initialize_with_no_words
()
{ // Vectors are empty, by default constructor construction.
  // Returning, makes solver not re-usable.
  return;
  
  abc2_data              & d     = this->m_data;
  ::std::vector<size_t>  * ht    = d.m_hash_table;
  size_t  i;
  for(i=0; HMAX > i; ++i)
  { ht[i].clear(); }
  
}
 
void
words_processing::add_words
()
{ //  Interation for all words:
  size_t           i;
  abc2_data      & d             = this->m_data;
  size_t     const z             = d.m_number_of_words;
  size_t     const n             = d.m_word_size;
  char           * pattern       = d.m_words_symbols; // the current word
  // Iteration for a word:
  size_t           j;
  size_t           sum;
  size_t           digit;
  char             symbol;
  
  ::std::vector<size_t>  * ht = d.m_hash_table;
   
  for(i=0; z>i; ++i)
  { // Add the number for (1+i)-th word, into the set.
    sum = 0;
    for(j=0; n>j; ++j)
    { symbol  = pattern[j];
      digit   = (static_cast<size_t>(symbol) );
      sum     = ((3*sum) + digit);
    }
    ht[sum % HMAX].push_back(sum);
    // Update position of the current word.
    pattern += n;
  }
  
}

/// \brief  Solves the abc2 problem, using the hash table.
class solver
{ private:
   
  abc2_data  & m_data;
   
  public:
   
  solver
  ( abc2_data  & data
  )
  : m_data(data)
  { this->process_words();
    this->count_number_of_occurrences();
  }
   
  private:
   
  void
  process_words
  ()
  ;
   
  void
  count_number_of_occurrences
  ()
  ;
  
} // class solver
;
 
void
solver::process_words
()
{ // Construct the finite automaton.
  words_processing  numbers_for_words_processing(m_data);
         // The constructor of numbers_for_words_processing needs
         // to be evaluated
         //   , because its evaluation contains side effects:
         //     it changes the state of the m_data object
         // .
  (void) numbers_for_words_processing;
}
 
void
solver::count_number_of_occurrences
()
{ typedef
    ::std::vector<size_t>::iterator
    ht_iterator
    ;
  
  // Count occurrences, using the set with numbers for words.
  size_t                    i;
  abc2_data               & d        = m_data;
  char                    * q        = d.m_text_symbols;
  size_t                    m        = d.m_text_size;
  size_t              const n        = d.m_word_size;
  size_t                    h        = 0; // The number of occurrences.
  size_t                    msd      = 1; // To remove most significant digit.
  size_t                    sum      = 0; 
  size_t                    digit;
  size_t                    hc;           // hash - code
  ht_iterator               i0;
  ht_iterator               i2;
  size_t                    w;
  ::std::vector<size_t>  * ht = d.m_hash_table;
   
  for(i=1; n>i; ++i)  { msd = (3*msd); }
   
  for(i=0; ((m>i) && (n>i)); ++i)
  { digit  = q[i];
    sum = ( (3*sum) + digit);
  }
  
  if(n == i)
  { hc = sum % HMAX;
    i0 = ht[hc].begin();
    i2 = ht[hc].end();
    while(i2 != i0)
    { w = ( *i0); ++i0;
      if(sum == w)
      { ++h;   // match
        break;
      }
    }
  }
   
  for( ; m>i; ++i)
  { digit  = q[i];
    sum = (sum % msd);
    sum = ( (3*sum) + digit);
    
    hc = sum % HMAX;
    i0 = ht[hc].begin();
    i2 = ht[hc].end();
    while(i2 != i0)
    { w = ( *i0); ++i0;
      if(sum == w)
      { ++h;   // match
        break;
      }
    }
  }
   
  // no-throw, no-fail
  ::std::size_t  & ans =
    d.m_number_of_occurrences;
  ans = h;
}

} // namespace yet_another_solver

// ______________________________________________________________________________

/// \brief  Reads input.
class input_reader
{ private:
  
  /// \brief  The abc2 data.
  abc2_data  & m_data;
  
  /// \brief  Processing is successful.
  bool  m_ok;
  
  public:
  
  input_reader
  ( abc2_data  & data
  , bool       & ok
  )
  : m_data(data)
  , m_ok(read_input() )
  { ok = m_ok;
  }
  
  private:
  
  bool
  read_input
  ()
  throw()
  ;
  
} // class input reader.
;

bool
input_reader::read_input
()
throw()
{ freopen("abc2.in", "rt", stdin);
  setvbuf(stdin , NULL , _IOFBF , maxm2 + maxv2 + maxn + 1000);
   
  abc2_data      & d = this->m_data;
  char           * q = d.m_text_symbols;
  ::std::size_t  & m = d.m_text_size;
  char           * p = d.m_words_symbols;
  ::std::size_t  & n = d.m_word_size;
  ::std::size_t  & z = d.m_number_of_words;
  ::std::size_t    i = 0;
  char           * y = q;
  ::std::size_t    w = 0;
  char           * s = 0;
   
  fgets(q, maxm2, stdin);
  while(    ('a' <= (*y))
         && ('c' >= (*y))
       )
  { (*y) = (*y) - 'a'; ++y; ++w; }
  m = w;
  q[m] = '\0';
   
  p[0] = '\0';
  y = p; w = 0;
  fgets(p, maxc2, stdin);
  while(    ('a' <= (*y))
         && ('c' >= (*y))
       )
  { (*y) = (*y) - 'a'; ++y; ++w; }
  n = w;
  if( (0 < n) && (1 <= maxn) )
  { i = 1; p += n; p[0] = '\0'; }
   
  s = fgets(p, maxc2, stdin);
  while(    (0 != s)
         && (i < maxn)
       )
  { y = p; w = 0;
    while(    ('a' <= (*y))
           && ('c' >= (*y))
         )
    { (*y) = (*y) - 'a'; ++y; ++w; }
    if(n == w)
    { ++i; p += n;  p[0] = '\0';
    }
     
    s = fgets(p, maxc2, stdin);
  }
   
  p[0] = '\0';
   
  z = i;
   
  return true; // Success.
}

// ______________________________________________________________________________
 
/// \brief  Writes output.
class output_writer
{ private:
   
  /// \brief  The abc2 data.
  abc2_data  & m_data;
  /// \brief  Processing was successful.
  bool         m_ok;
  
  public:
  
  output_writer
  ( abc2_data  & data
  , bool       & ok
  )
  : m_data(data)
  , m_ok(write_output() )
  { ok = m_ok;
  }
  
  private:
  
  bool
  write_output
  ()
  throw()
  ;
  
} // class output_writer
;

bool
output_writer::write_output
()
throw()
{ freopen("abc2.out", "wt", stdout);
   
  abc2_data  & d = this->m_data;
  size_t const  & number_of_occurrences =
    d.m_number_of_occurrences;
  printf("%u\n", number_of_occurrences);
  return true; // Success.
}

} // namespace abc2


/// The spaced used by the program.
static
::abc2::abc2_data
s_data;
 
/// Because the abc2_data is plain old data
/// , the construction of s_data is guaranteed
///   not to throw a c++ exception
/// .
/// True once, fails to be true, with the addition
/// of the ::std::vector<size_t> data member
/// , but no c++ exception is still true.

bool
solve_the_abc2_problem
()
{ ::abc2::abc2_data  & d = s_data;
  
  bool  ok;
  
  ::abc2::input_reader  r(d, ok);
  if( !ok)
  { return false; } // Failure.    No input.
  (void) r;
  
  size_t  cnst1 = ::abc2::maxc;
  bool    cond1 = (cnst1>d.m_word_size);
  (void) cond1;
  (void) cond1;
  
  size_t  cnst2 = 3* ( ::abc2::maxv2 / 4);
  size_t  k1 = d.m_number_of_words;
  size_t  k2 = d.m_word_size;
  k1 = k1 * k2;
  bool  cond2 = (k1 > cnst2);
  
  if(cond2)
  { // The DFA solution is awesome.
    ::abc2::solver  s(d);
    (void) s;
  }
  else
  { ::abc2::yet_another_solver::solver  s(d);
    (void) s;
  }
  
  ::abc2::output_writer  w(d, ok);
  if( !ok)
  { return false; } // Failure.   No output.
  
  return true; // Success.
}

int
main
()
{ int  status;
  try
  { bool  ok     = solve_the_abc2_problem();
          status = ( ok ? 0 : 1 );
  }
  catch ( ... )
  { status = 2;
  }
  
  return status;
}