生活随笔
收集整理的這篇文章主要介紹了
hihoCoder #1457 : 后缀自动机四·重复旋律7
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
題目鏈接
描述
小Hi平時的一大興趣愛好就是演奏鋼琴。我們知道一段音樂旋律可以被表示為一段數構成的數列。
神奇的是小Hi發現了一部名字叫《十進制進行曲大全》的作品集,顧名思義,這部作品集里有許多作品,但是所有的作品有一個共同特征:只用了十個音符,所有的音符都表示成0-9的數字。
現在小Hi想知道這部作品中所有不同的旋律的“和”(也就是把串看成數字,在十進制下的求和,允許有前導0)。答案有可能很大,我們需要對(10^9 + 7)取摸。
輸入
第一行,一個整數N,表示有N部作品。
接下來N行,每行包含一個由數字0-9構成的字符串S。
所有字符串長度和不超過 1000000。
輸出
共一行,一個整數,表示答案 mod (10^9 + 7)。
樣例輸入
2
101
09
樣例輸出
131
思路
后綴自動機板子
-
首先考慮只有一個字符串的情況,例如:S=“1122124”
-
當我們要求sum6sum_6sum6?,觀察自動機的trans函數發現狀態6可以由狀態狀態4和狀態5轉移過來,我們知道狀態6的字符串是由狀態5和狀態4后面添1得到的,當我們知道狀態4,5的sum和狀態數,轉移邊就可以直接求解狀態6
Str6=(Str4+Str5)+′1′Str_6 = (Str_4+Str_5)+ '1'Str6?=(Str4?+Str5?)+′1′
Sum6=(Sum4+Sum5)?10+num1{Sum_6 = (Sum_4+Sum_5)*10 + num_1}Sum6?=(Sum4?+Sum5?)?10+num1?
-
狀態數對應自動機節點的入度,我們對節點進行拓撲排序,一邊計算節點入度(狀態數),一遍計算sum。
-
對于N個字符串的情況,我們可以把字符串用":“拼接,”:"的ASCII為10,方便處理
#include <bits/stdc++.h>
#define LL long long
#define P pair<int, int>
#define lowbit(x) (x & -x)
#define mem(a, b) memset(a, b, sizeof(a))
#define rep(i, a, n) for (int i = a; i <= n; ++i)
const int maxn
= 1000005;
#define mid ((l + r) >> 1)
#define lc rt<<1
#define rc rt<<1|1
using namespace std
;
const LL mod
= 1e9 + 7;
struct SAM
{int trans
[maxn
<<1][26], slink
[maxn
<<1], maxlen
[maxn
<<1];int indegree
[maxn
<<1], endpos
[maxn
<<1], rank
[maxn
<<1], ans
[maxn
<<1];LL sum
[maxn
<<1];int last
, now
, root
, len
;inline void newnode
(int v
) {maxlen
[++now
] = v
;}inline void extend(int c
) {newnode(maxlen
[last
] + 1);int p
= last
, np
= now
;while (p
&& !trans
[p
][c
]) {trans
[p
][c
] = np
;p
= slink
[p
];}if (!p
) slink
[np
] = root
;else {int q
= trans
[p
][c
];if (maxlen
[p
] + 1 != maxlen
[q
]) {newnode(maxlen
[p
] + 1);int nq
= now
;memcpy(trans
[nq
], trans
[q
], sizeof(trans
[q
]));slink
[nq
] = slink
[q
];slink
[q
] = slink
[np
] = nq
;while (p
&& trans
[p
][c
] == q
) {trans
[p
][c
] = nq
;p
= slink
[p
];} }else slink
[np
] = q
;}last
= np
;endpos
[np
] = 1;}inline void build(char *s
) {len
= strlen(s
);root
= last
= now
= 1;for (int i
= 0; i
< len
; ++i
) extend(s
[i
] - '0'); }inline LL
getSum() {for (int i
= 1; i
<= now
; ++i
) indegree
[ maxlen
[i
] ]++;for (int i
= 1; i
<= now
; ++i
) indegree
[i
] += indegree
[i
-1];for (int i
= 1; i
<= now
; ++i
) rank
[ indegree
[ maxlen
[i
] ]-- ] = i
;mem(endpos
, 0);endpos
[1] = 1; for (int i
= 1; i
<= now
; ++i
) {int x
= rank
[i
];for (int j
= 0; j
< 10; ++j
) {int nex
= trans
[x
][j
];if (!nex
) continue;endpos
[nex
] += endpos
[x
]; LL num
= (sum
[x
] * 10 + endpos
[x
] * j
) % mod
; sum
[nex
] = (sum
[nex
] + num
) % mod
; }}LL ans
= 0;for (int i
= 2; i
<= now
; ++i
) ans
= (ans
+ sum
[i
]) % mod
;return ans
;}inline void getEndpos() {for (int i
= 1; i
<= now
; ++i
) indegree
[ maxlen
[i
] ]++; for (int i
= 1; i
<= now
; ++i
) indegree
[i
] += indegree
[i
-1]; for (int i
= 1; i
<= now
; ++i
) rank
[ indegree
[ maxlen
[i
] ]-- ] = i
; for (int i
= now
; i
>= 1; --i
) {int x
= rank
[i
];endpos
[slink
[x
]] += endpos
[x
];}}inline LL all
() {LL ans
= 0;for (int i
= root
+1; i
<= now
; ++i
) {ans
+= maxlen
[i
] - maxlen
[ slink
[i
] ];}return ans
;}inline void get_Maxk() {getEndpos();for (int i
= 1; i
<= now
; ++i
) {ans
[maxlen
[i
]] = max(ans
[maxlen
[i
]], endpos
[i
]);}for (int i
= len
; i
>= 1; --i
) ans
[i
] = max(ans
[i
], ans
[i
+1]);for (int i
= 1; i
<= len
; ++i
) printf("%d\n", ans
[i
]);}}sam
;
char s
[maxn
];
int main() {
#ifndef ONLINE_JUDGE
#endifios
::sync_with_stdio(false);cin
.tie(0); cout
.tie(0);int n
;scanf("%d", &n
);int pos
= 0;while (n
--) {scanf("%s", s
+pos
);pos
= strlen(s
);s
[pos
] = ':';pos
++;}s
[pos
-1] = '\0';sam
.build(s
);printf("%lld\n", sam
.getSum());return 0;
}
總結
以上是生活随笔為你收集整理的hihoCoder #1457 : 后缀自动机四·重复旋律7的全部內容,希望文章能夠幫你解決所遇到的問題。
如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。