2021-10-11 T1 string(P5329)
题目传送门:传送门
思维题,赛时写了 $O(n^2)$的暴力,得了 $10pts$ 跑路,被全机房吊打qaq。赛后发现有很多不同的解法,大部分人使用了 $O(nlogn)$ 的算法(杜佬: $merge sort O(nlogn^2)$ 不能过吗??? )。希望写一个 $O(n)$ 算法的我去问了 fxj 学哥,他提示我前面互不相同的那个特殊结论可能会对正解有一定帮助。今天再做的时候,受这个启发,得到了一个贪心策略。
如果当前字符两两不同,考虑除去第 $i$ 个与第 $i-1$ 个的比较,显然只有大于和小于两种情况。前面部分是一样的,这一位分别是这两个字符互换,显然大的那个排名靠前。如果第 $i$ 个大于第 $i-1$ 个,这时直接把 $i-1$ 放到当前记录的头队列的末尾。反之,如果第 $i$ 个小于第 $i-1$个,把 $i-1$ 放到尾队列的队首。证明的话,显然后面还未比较的字符串的前缀都和第 $i$ 个一样,所以与第 $i-1$ 个的比较结果也和第 $i$ 个一样,所以操作是合理的。时间复杂度 $O(n)$。
再来考虑可以相同的情况,所有相同的与后面带比较的字符串就与最后一个的比较结果相同,而编号小的在前,所以只要把连续的相同的顺序记录,然后当做一个整体做上面特殊情况的操作即可。每个都只会被访问 O(1) 次,所以时间复杂度为 $O(n)$。
1 #include<bits/stdc++.h> 2 using namespace std; 3 4 const int BUF_SIZE = 1 << 20; 5 char buf[BUF_SIZE] , *curP = buf; 6 void buf_flush() { 7 if(curP != buf) { 8 fwrite(buf , (curP - buf) , 1 , stdout); 9 curP = buf; 10 } 11 } 12 void putChar(char c) { 13 *curP++ = c; 14 if(curP == buf + BUF_SIZE) 15 buf_flush(); 16 } 17 void write(int x) { 18 static char stk[13]; 19 char* p = stk + 12 , *ed = stk + 12; 20 while(x) { 21 *--p = x % 10 + '0'; 22 x /= 10; 23 } 24 while(p != ed) putChar(*p++); 25 } 26 27 const int N = 1000000 + 10; 28 int n; 29 char s[N]; 30 int ans[N]; 31 signed int main() 32 { 33 freopen("string.in","r",stdin); 34 freopen("string.out","w",stdout); 35 scanf("%d",&n); 36 scanf("%s",s + 1); 37 int l = 0,r = n + 1,pos = 1; 38 for(int i = 2;i <= n;i++) 39 { 40 if(s[i] == s[i-1]) continue; 41 if(s[i] < s[i-1]) 42 { 43 for(int j = pos;j < i;j++) 44 ans[++l] = j; 45 pos = i; 46 } 47 else 48 { 49 for(int j = i - 1;j >= pos;j--) 50 ans[--r] = j; 51 pos = i; 52 } 53 } 54 for(int i = pos;i <= n;i++) 55 ans[++l] = i; 56 for(int i = 1;i <= n;i++) 57 write(ans[i]),putChar(' '); 58 putChar('\n'); 59 buf_flush(); 60 return 0; 61 }

浙公网安备 33010602011771号