You have an array a[1], a[2], ..., a[n], containing distinct integers from1 ton. Your task is to sort this array in increasing order with the following operation (you may need to apply it multiple times):
- choose two indexes, i and j (1 ≤ i < j ≤ n;(j - i + 1) is a prime number);
- swap the elements on positions i and j; in other words, you are allowed to apply the following sequence of assignments:tmp = a[i], a[i] = a[j], a[j] = tmp (tmp is a temporary variable).
You do not need to minimize the number of used operations. However, you need to make sure that there are at most5n operations.
The first line contains integer n (1 ≤ n ≤ 105). The next line containsn distinct integersa[1], a[2], ..., a[n](1 ≤ a[i] ≤ n).
In the first line, print integer k (0 ≤ k ≤ 5n) — the number of used operations. Next, print the operations. Each operation must be printed as "ij" (1 ≤ i < j ≤ n;(j - i + 1) is a prime).
If there are multiple answers, you can print any of them.
3
3 2 1
1
1 3
2
1 2
0
4
4 2 3 1
3 2 4 1 2 2 4 题意:每次交换两个数,要求这两个数的位置差,是素数。 思路:根据哥德巴赫猜想,一个合数可以表示成最多5个素数的和,所以j-i+1最多表示成5个素数,保证最多交换5*n次。先打素数表,然后从最小的数字一个个模拟往前放即可,放的时候走的步数直接拆成都是质数即可。#include<iostream> #include<cstdio> #include<cstring> #include<vector> #include<map> #include<cmath> #include<algorithm> using namespace std; const int maxn=100010; bool vis[maxn]; int n,a[maxn],w[maxn]; void init() { memset(vis,0,sizeof(vis)); int k=sqrt(n); vis[1]=1; for(int i=2;i<=k;i++) { if(vis[i])continue; for(int j=i*i;j<=n;j+=i) vis[j]=1; } } int main() { scanf("%d",&n); init(); for(int i=1;i<=n;i++) { scanf("%d",&a[i]); w[a[i]]=i; } int cur=1; int ans=0; vector<pair<int,int> > v; while(cur<=n) { while(w[cur]!=cur) { for(int j=cur;;j++) { if(!vis[w[cur]-j+1]) { v.push_back(make_pair(j,w[cur])); ans++; int tmp=w[cur]; w[cur]=j; w[a[j]]=tmp; swap(a[tmp],a[j]); break; } } } cur++; } printf("%d\n",ans); for(int i=0;i<ans;i++) cout<<v[i].first<<" "<<v[i].second<<endl; return 0; }
题意:给你一个长度不超过10^5的字符串。要你按长度输出和后缀完全匹配的的前缀的长度。和该前缀在整个串中出现的次数。D. Prefixes and SuffixesYou have a string s = s1s2...s|s|, where |s| is the length of string s, and si its i-th character.
Let's introduce several definitions:
- A substring s[i..j] (1 ≤ i ≤ j ≤ |s|) of string s is string sisi + 1...sj.
- The prefix of string s of length l (1 ≤ l ≤ |s|) is string s[1..l].
- The suffix of string s of length l (1 ≤ l ≤ |s|) is string s[|s| - l + 1..|s|].
Your task is, for any prefix of string s which matches a suffix of string s, print the number of times it occurs in string s as a substring.
InputThe single line contains a sequence of characters s1s2...s|s| (1 ≤ |s| ≤ 105) — string s. The string only consists of uppercase English letters.
OutputIn the first line, print integer k (0 ≤ k ≤ |s|) — the number of prefixes that match a suffix of string s. Next print k lines, in each line print two integers li ci. Numbers li ci mean that the prefix of the length li matches the suffix of length li and occurs in string s as a substring ci times. Print pairs li ci in the order of increasing li.
Sample test(s)InputABACABA
Output3 1 4 3 2 7 1
InputAAA
Output3 1 3 2 2 3 1
思路:我使用扩展KMP做的,先求出next数组,如果next[i]==n-i,则表示前缀有后缀相同,然后输出前缀出线的次数(也可以用next数组得到)#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> using namespace std; const int maxn=100010; char s[maxn]; int next[maxn],n; int cnt[maxn],ans[maxn]; void getnext() { n=strlen(s); next[0]=n; int j=0; while(j+1<n&&s[j]==s[j+1])j++; next[1]=j; int k=1; for(int i=2;i<n;i++) { int p=k+next[k]-1; int l=next[i-k]; if(i+l<p+1)next[i]=l; else { j=max(0,p-i+1); while(i+j<n&&s[i+j]==s[j])j++; next[i]=j; k=i; } } } int main() { //freopen("in.txt","r",stdin); scanf("%s",s); getnext(); memset(cnt,0,sizeof(cnt)); for(int i=0;i<n;i++) if(next[i]) cnt[next[i]]++; int x=cnt[n]; for(int i=n-1;i>=1;i--)//统计长度为i的前缀出现的次数 { if(cnt[i]==0)continue; cnt[i]+=x; x=cnt[i]; } int num=0; for(int i=0;i<n;i++) { if(next[i]==n-i)ans[num++]=next[i];//判断是不是前缀与后缀相同 } sort(ans,ans+num); printf("%d\n",num); for(int i=0;i<num;i++) { printf("%d %d\n",ans[i],cnt[ans[i]]); } return 0; }
也可以直接用kmp做,具体详见代码
#include<iostream> #include<cstdio> #include<cstring> #define INF 0x3f3f3f3f using namespace std; const int N = 100005; char s[N]; int next[N], n, ans[N], ansn = 0; void get_next(char *seq, int m) { int len=strlen(seq); next[0]=-1; int i=0,j=-1; while(i<n) { if(j==-1||seq[i]==seq[j]) { i++; j++; next[i]=j; } else j=next[j]; } } int vis[N]; int main() { //freopen("in.txt","r",stdin); int i = 0; scanf("%s", s + 1); n = strlen(s + 1); get_next(s, n); int t = next[n]; while (t) { ans[ansn++] = t;//next表示适配后应该从那个开始在跟当前位置比较,即前缀有多少长度与当前后缀匹配 t = next[t]; } for (i = n; i > 0; i--) vis[next[i]]++; for (i = n; i > 0; i--) vis[next[i]] += vis[i];//前缀出线的次数 printf("%d\n", ansn + 1); for (i = ansn - 1; i >= 0; i--) printf("%d %d\n", ans[i], vis[ans[i]] + 1); printf("%d %d\n", n, vis[n] + 1); return 0; }