String & dp Problem Round 3 2017.4.22
对每一个特征求前缀和,如果它减去前面的某一个地方的和,得到的每个特征是相等的,那么然后就可以更新答案。
需要解决这个两个问题
1.如何使答案尽量大?
这个很简单,直接找尽量靠前的地方就好了。
2,如何快速查找?
考虑用后一项减去前一项得到的新的序列,
然后就可以转换成找一个相等的序列,这个Hash就可以搞定。
Code
1 #include<iostream> 2 #include<fstream> 3 #include<sstream> 4 #include<cstdio> 5 #include<cstring> 6 #include<set> 7 #ifndef WIN32 8 #define Auto "%lld" 9 #else 10 #define Auto "%I64d" 11 #endif 12 using namespace std; 13 typedef bool boolean; 14 #define smin(a, b) (a) = min((a), (b)) 15 #define smax(a, b) (a) = max((a), (b)) 16 17 typedef class Feature { 18 public: 19 int k; 20 int lis[35]; 21 22 Feature() { } 23 Feature(int x, int k):k(k) { 24 for(int i = 0; i < k; i++) 25 if(x & (1 << i)) 26 lis[i] = 1; 27 else 28 lis[i] = 0; 29 } 30 31 friend boolean operator == (Feature& a, Feature& b) { 32 for(int i = 0; i < a.k; i++) 33 if(a.lis[i] != b.lis[i]) 34 return false; 35 return true; 36 } 37 }Feature; 38 39 template<typename Key, typename Val> 40 class LinkedNode { 41 public: 42 Key key; 43 Val val; 44 int next; 45 LinkedNode() { } 46 LinkedNode(Key key, Val val, int next):key(key), val(val), next(next) { } 47 }; 48 49 template<typename Key, typename Val> 50 class HashMap{ 51 protected: 52 const static int moder = 500007; 53 const static int C = 8127; 54 int hashCode(Feature x) { 55 int c = 1; 56 int hash = 0; 57 for(int i = 0; i < x.k; i++) { 58 hash = ((long long)hash + (x.lis[i] * 1LL * c) % moder + moder) % moder; 59 c = (c * 1LL * C) % moder; 60 } 61 return hash; 62 } 63 public: 64 int cn; 65 int h[(moder + 1)]; 66 LinkedNode<Key, Val> *lis; 67 68 HashMap():cn(0) { } 69 HashMap(int limit):cn(0) { 70 lis = new LinkedNode<Key, Val>[(const int)(limit + 1)]; 71 memset(h, 0, sizeof(h)); 72 } 73 74 inline void insert(Key& k, Val v) { 75 int hash = hashCode(k); 76 lis[++cn] = LinkedNode<Key, Val>(k, v, h[hash]); 77 h[hash] = cn; 78 } 79 80 inline Val find(Key& k) { 81 int hash = hashCode(k); 82 for(int i = h[hash]; i; i = lis[i].next) { 83 if(lis[i].key == k) 84 return lis[i].val; 85 } 86 return -1; 87 } 88 }; 89 90 ifstream fin("feature.in"); 91 ofstream fout("feature.out"); 92 93 int n, k; 94 int *a; 95 96 inline void init() { 97 fin >> n >> k; 98 a = new int[(const int)(n + 1)]; 99 for(int i = 1; i <= n; i++) { 100 fin >> a[i]; 101 } 102 } 103 104 int res = 0; 105 Feature org; 106 Feature sumer; 107 Feature cmp; 108 HashMap<Feature, int> s; 109 inline void solve() { 110 s = HashMap<Feature, int>(n + 5); 111 org = Feature(0, k - 1); 112 sumer = Feature(0, k); 113 cmp.k = k - 1; 114 s.insert(org, 0); 115 for(int i = 1; i <= n; i++) { 116 for(int j = 0; j < k; j++) 117 if(a[i] & (1 << j)) 118 sumer.lis[j]++; 119 for(int j = 0; j < k - 1; j++) 120 cmp.lis[j] = sumer.lis[j + 1] - sumer.lis[j]; 121 int x = s.find(cmp); 122 if(x == -1) 123 s.insert(cmp, i); 124 else 125 smax(res, i - x); 126 } 127 fout << res; 128 } 129 130 int main() { 131 init(); 132 solve(); 133 return 0; 134 }
这个肯定是要Kmp,所以可以考虑Kmp。
比起朴素的Kmp,多记录一些东西:
fail[] 在文本串i位置匹配时对应模板串的位置j
last[] 文本串实际有效的字符所构成的链表,这个记录文本串的位置i的前驱
对于找到的一个串,沿着last往回找,找到的点打标记,然后把匹配到的位置的下一个last改成这个串的起点的前一个,把当前匹配到的位置改为起点前一个的fail值。
最后找一遍标记处理一下就可以ac了。
因为每个字符最多被访问两次(kmp + 打标记),所以总时间复杂度为O(2n + m),其中n为文本串的长度,m为模板串的长度。
Code
1 #include<iostream> 2 #include<fstream> 3 #include<sstream> 4 #include<cstdio> 5 #include<cstring> 6 #include<set> 7 #ifndef WIN32 8 #define Auto "%lld" 9 #else 10 #define Auto "%I64d" 11 #endif 12 using namespace std; 13 typedef bool boolean; 14 #define smin(a, b) (a) = min((a), (b)) 15 #define smax(a, b) (a) = max((a), (b)) 16 17 int lenS, lenT; 18 char T[300005]; 19 char S[300005]; 20 boolean enable[300005]; 21 char newS[300005]; 22 23 inline void init() { 24 scanf("%s", T); 25 scanf("%s", S); 26 lenS = strlen(S); 27 lenT = strlen(T); 28 } 29 30 int f[300005]; 31 inline void getFail() { 32 int j = 0; 33 f[0] = 0; 34 f[1] = 0; 35 for(int i = 1; i < lenT; i++) { 36 j = f[i]; 37 while(j && T[i] != T[j]) j = f[j]; 38 f[i + 1] = (T[i] == T[j]) ? (j + 1) : (0); 39 } 40 } 41 42 int fail[300005]; 43 int last[300005]; 44 inline void kmp() { 45 getFail(); 46 int j = 0; 47 memset(enable, true, sizeof(boolean) * (lenS + 1)); 48 for(int i = 0; i < lenS; i++) last[i] = i - 1; 49 for(int i = 0; i < lenS; i++) { 50 while(j && S[i] != T[j]) j = f[j]; 51 if(S[i] == T[j]) j++; 52 fail[i] = j; 53 if(j == lenT) { 54 int l = i; 55 for(int cnt = 0; cnt < lenT; cnt++) 56 enable[l] = false, l = last[l]; 57 if(l == -1) j = 0; 58 else j = fail[l]; 59 last[i + 1] = l; 60 } 61 } 62 } 63 64 inline void solve() { 65 kmp(); 66 int top = 0; 67 for(int i = 0; i < lenS; i++) { 68 if(enable[i]) 69 newS[top++] = S[i]; 70 } 71 newS[top] = 0; 72 puts(newS); 73 } 74 75 int main() { 76 freopen("sensitive.in", "r", stdin); 77 freopen("sensitive.out", "w", stdout); 78 init(); 79 solve(); 80 return 0; 81 }
用AC自动机,然后差分数组去优化(虽然没快多少)
Code
1 #include<iostream> 2 #include<fstream> 3 #include<sstream> 4 #include<cstdio> 5 #include<cstring> 6 #include<set> 7 #include<queue> 8 #ifndef WIN32 9 #define Auto "%lld" 10 #else 11 #define Auto "%I64d" 12 #endif 13 using namespace std; 14 typedef bool boolean; 15 #define smin(a, b) (a) = min((a), (b)) 16 #define smax(a, b) (a) = max((a), (b)) 17 18 #define CharSet 26 19 20 typedef class TrieNode { 21 public: 22 int val; 23 TrieNode* next[CharSet]; 24 TrieNode* last; 25 TrieNode* fail; 26 TrieNode():val(0), last(NULL), fail(NULL) { 27 memset(next, 0, sizeof(next)); 28 } 29 }TrieNode; 30 31 int cti(char x) { 32 if(x >= 'A' && x <= 'Z') return x - 'A'; 33 if(x >= 'a' && x <= 'z') return x - 'a'; 34 return -1; 35 } 36 37 typedef class Trie { 38 public: 39 TrieNode* root; 40 41 Trie() { 42 root = new TrieNode(); 43 } 44 45 inline void insert(char* s) { 46 int len = strlen(s); 47 TrieNode* p = root; 48 for(int i = 0; i < len; i++) { 49 int c = cti(s[i]); 50 if(p->next[c] == NULL) { 51 p->next[c] = new TrieNode(); 52 } 53 p = p->next[c]; 54 } 55 p->val = len; 56 } 57 }Trie; 58 59 typedef class AhoCorasick { 60 public: 61 Trie t; 62 63 inline void insert(char* s) { 64 t.insert(s); 65 } 66 67 inline void getFail() { 68 queue<TrieNode*> que; 69 t.root->fail = t.root; 70 for(int i = 0; i < CharSet; i++) 71 if(t.root->next[i] != NULL) { 72 t.root->next[i]->fail = t.root; 73 que.push(t.root->next[i]); 74 } 75 while(!que.empty()) { 76 TrieNode* e = que.front(); 77 que.pop(); 78 for(int i = 0; i < CharSet; i++) { 79 TrieNode* eu = e->next[i]; 80 if(eu == NULL) continue; 81 TrieNode* f = e->fail; 82 while(f != t.root && f->next[i] == NULL) f = f->fail; 83 eu->fail = (f->next[i]) ? (f->next[i]) : (t.root); 84 eu->last = (eu->fail->val) ? (eu->fail) : (eu->fail->last); 85 que.push(eu); 86 } 87 } 88 } 89 90 int findLast(TrieNode* p) { 91 if(p) { 92 int ret = findLast(p->last); 93 return max(p->val, ret); 94 } 95 return 0; 96 } 97 98 inline void change(int* enable, int i, int len) { 99 enable[i + 1] += -1; 100 enable[i - len + 1] += 1; 101 } 102 103 inline void find(char* s, int* enable) { 104 int len = strlen(s); 105 TrieNode* f = t.root; 106 for(int i = 0; i < len; i++) { 107 int c = cti(s[i]); 108 if(c == -1) { 109 f = t.root; 110 continue; 111 } 112 while(f != t.root && f->next[c] == NULL) f = f->fail; 113 if(f->next[c]) f = f->next[c]; 114 if(f->val) change(enable, i, f->val); 115 else if(f->last) change(enable, i, findLast(f->last)); 116 } 117 } 118 }AhoCorasick; 119 120 int n; 121 char s[300005]; 122 int enable[300005]; 123 AhoCorasick ac; 124 125 inline void init() { 126 scanf("%d", &n); 127 for(int i = 1; i <= n; i++) { 128 scanf("%s", s); 129 ac.insert(s); 130 } 131 getchar(); 132 gets(s); 133 } 134 135 inline void solve() { 136 int len = strlen(s); 137 memset(enable, 0, sizeof(boolean) * (len + 1)); 138 ac.getFail(); 139 ac.find(s, enable); 140 for(int i = 0; i < len; i++) { 141 if(i) enable[i] += enable[i - 1]; 142 if(enable[i]) 143 s[i] = '*'; 144 } 145 puts(s); 146 } 147 148 int main() { 149 freopen("cleanse.in", "r", stdin); 150 freopen("cleanse.out", "w", stdout); 151 init(); 152 solve(); 153 return 0; 154 }
通过子串可以想到后缀自动机(虽然标程用的后缀数组,但是我不会QAQ)
然后上面进行一个拓扑排序,记录根节点(空状态)到每个节点(状态)的总的方案数和经过边权为a的方案数。
最后把所有的节点的方案数加起来就好了。
Code
1 #include<iostream> 2 #include<fstream> 3 #include<sstream> 4 #include<cstdio> 5 #include<cstring> 6 #include<set> 7 #include<queue> 8 #ifndef WIN32 9 #define Auto "%lld" 10 #else 11 #define Auto "%I64d" 12 #endif 13 using namespace std; 14 typedef bool boolean; 15 #define smin(a, b) (a) = min((a), (b)) 16 #define smax(a, b) (a) = max((a), (b)) 17 18 #define CharSet 26 19 20 int cti(char x) { 21 return x - 'a'; 22 } 23 24 typedef class TrieNode { 25 public: 26 int len; 27 int dag; 28 long long cnt; 29 long long cnta; 30 TrieNode* next[CharSet]; 31 TrieNode* fail; 32 TrieNode(int len = 0):fail(NULL), len(len), cnt(0), dag(0), cnta(0) { 33 memset(next, 0, sizeof(next)); 34 } 35 }TrieNode; 36 37 typedef class SuffixAutomation { 38 public: 39 TrieNode* pool; 40 TrieNode* top; 41 TrieNode* root; 42 TrieNode* last; 43 44 TrieNode* newnode(int len) { 45 top->len = len; 46 return top++;; 47 } 48 49 SuffixAutomation() { } 50 SuffixAutomation(int n) { 51 pool = new TrieNode[(2 * n + 5)]; 52 top = pool; 53 root = top++; 54 last = root; 55 } 56 57 inline void extends(char ch) { 58 int c = cti(ch); 59 TrieNode* node = newnode(last->len + 1); 60 TrieNode* f = last; 61 while(f && f->next[c] == NULL) 62 f->next[c] = node, f = f->fail; 63 if(!f) node->fail = root; 64 else { 65 TrieNode* p = f->next[c]; 66 if(p->len == f->len + 1) 67 node->fail = p; 68 else { 69 TrieNode* q = newnode(f->len + 1); 70 memcpy(q->next, p->next, sizeof(q->next)); 71 q->fail = p->fail; 72 p->fail = q; 73 node->fail = q; 74 while(f && f->next[c] == p) 75 f->next[c] = q, f = f->fail; 76 } 77 } 78 last = last->next[c]; 79 } 80 }SuffixAutomation; 81 82 int n; 83 char S[100005]; 84 SuffixAutomation sam; 85 86 inline void init() { 87 gets(S); 88 n = strlen(S); 89 sam = SuffixAutomation(n); 90 } 91 92 void getDag() { 93 for(TrieNode* p = sam.pool; p != sam.top; p++) { 94 for(int i = 0; i < CharSet; i++) 95 if(p->next[i]) 96 p->next[i]->dag++; 97 } 98 } 99 100 queue<TrieNode*> que; 101 void bfs() { 102 sam.root->cnt = 1; 103 que.push(sam.root); 104 while(!que.empty()) { 105 TrieNode* e = que.front(); 106 que.pop(); 107 if(e->next[0]) { 108 e->next[0]->dag--, e->next[0]->cnta += e->cnt, e->next[0]->cnt += e->cnt; 109 if(!e->next[0]->dag) 110 que.push(e->next[0]); 111 } 112 for(int i = 1; i < CharSet; i++) { 113 TrieNode* eu = e->next[i]; 114 if(eu) { 115 eu->dag--; 116 if(!eu->dag) 117 que.push(eu); 118 eu->cnt += e->cnt; 119 eu->cnta += e->cnta; 120 } 121 } 122 } 123 } 124 125 long long res = 0; 126 inline void solve() { 127 for(int i = 0; i < n; i++) 128 sam.extends(S[i]); 129 getDag(); 130 bfs(); 131 for(TrieNode* p = sam.pool; p != sam.top; p++) { 132 res += p->cnta; 133 } 134 printf(Auto, res); 135 } 136 137 int main() { 138 freopen("substring.in", "r", stdin); 139 freopen("substring.out", "w", stdout); 140 init(); 141 solve(); 142 return 0; 143 }