K transpositions - dp - 拉格朗日插值

题目大意:对于长度为n的a[i]=i的数列,每次可以选择两个数字交换,问交换不超过k次之后会得到多少不同的排列。

首先考虑一个朴素dp,即dp[i][j]表示序列长度是i交换次数是j的答案,那么显然要么i不参与交换,即前n-1个元素自己玩;或者n最后在p位置,然后相当与前n-1个位置自己玩,但是n凑合了一次交换次数。
因此转移有: dp[i][j]=dp[i1][j]+(i1)dp[i1][j1] d p [ i ] [ j ] = d p [ i − 1 ] [ j ] + ( i − 1 ) d p [ i − 1 ] [ j − 1 ]
注意到如果固定交换次数,然后将dp数值看作关于序列长度的函数,即 fk(n) f k ( n ) ,那么: fk(n)=fk(n1)+(n1)fk1(n1) f k ( n ) = f k ( n − 1 ) + ( n − 1 ) f k − 1 ( n − 1 ) 或者干脆: fk(x)=fk(x1)+(x1)fk1(x1) f k ( x ) = f k ( x − 1 ) + ( x − 1 ) f k − 1 ( x − 1 ) ,还是考虑归纳,当k=1时答案是x(x-1)/2,是个二次多项式;然后后半部分根据归纳就是2k-1次多项式,f_k就相当于是做这个2k-1次多项式的前缀和,因此是2k次的。暴力比较小的情况拉格朗日插值即可。
好像还有直接计数的办法,没有仔细研究。

#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
#define K 6020
#define lint long long
#define mod 1000000007
using namespace std;
int f[2][K],ps[K],ss[K],fac[K],facinv[K];lint xs[3];
inline int inv(int x,int k=mod-2,int ans=1) { for(;k;k>>=1,x=(lint)x*x%mod) (k&1)?ans=(lint)ans*x%mod:0;return ans; }
inline int mol(lint x) { return x%=mod,(x<0?x+mod:x); }
int main()
{
    int n,k,pre=0,now=1,ans=0;scanf("%d%d",&n,&k);
    for(int i=0;i<=2*k+1;i++) f[pre][i]=1;f[now][0]=1;
    for(int i=1;i<=k;i++,swap(now,pre))
        for(int j=1;j<=2*k+1;j++)
            f[now][j]=(f[now][j-1]+(j-1ll)*f[pre][j-1])%mod;
    swap(now,pre),ps[0]=ss[2*k+2]=1,xs[0]=1ll,xs[1]=-1ll;
    if(n<=2*k+1) return !printf("%d\n",f[now][n]);
    for(int i=1;i<=2*k+1;i++) ps[i]=(lint)ps[i-1]*mol(n-i)%mod;
    for(int i=2*k+1;i>=1;i--) ss[i]=(lint)ss[i+1]*mol(n-i)%mod;
    for(int i=fac[0]=1;i<=2*k+1;i++) fac[i]=(lint)fac[i-1]*i%mod;
    for(int i=facinv[0]=1;i<=2*k+1;i++) facinv[i]=inv(fac[i]);
    for(int i=1;i<=2*k+1;i++)
        ans=mol(ans+xs[(i+1)&1]*f[now][i]*ps[i-1]%mod*ss[i+1]%mod*facinv[i-1]%mod*facinv[2*k+1-i]);
    return !printf("%d\n",ans);
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
贾法里算法是一种字符串匹配算法,用于在一个文本串中查找一个模式串的出现位置。以下是贾法里算法的Java实现代码: ```java public class JaroWinklerDistance { public static double similarity(final String s1, final String s2) { if (s1.equals(s2)) { return 1.0; } final int window = Math.max(0, Math.max(s1.length(), s2.length()) / 2 - 1); final String s1Matches = getMatchingCharacters(s1, s2, window); final String s2Matches = getMatchingCharacters(s2, s1, window); if (s1Matches.length() == 0 || s2Matches.length() == 0) { return 0.0; } final int transpositions = getTranspositions(s1Matches, s2Matches); final double jaro = ((double) s1Matches.length() / s1.length() + (double) s2Matches.length() / s2.length() + (double) (s1Matches.length() - transpositions / 2.0) / s1Matches.length()) / 3.0; final double jaroWinkler = jaro + (getCommonPrefixLength(s1, s2) * 0.1 * (1.0 - jaro)); return jaroWinkler; } private static String getMatchingCharacters(final String s1, final String s2, final int window) { final StringBuilder common = new StringBuilder(); final StringBuilder copy = new StringBuilder(s2); for (int i = 0; i < s1.length(); i++) { final char ch = s1.charAt(i); boolean found = false; for (int j = Math.max(0, i - window); !found && j < Math.min(i + window, s2.length()); j++) { if (copy.charAt(j) == ch) { found = true; common.append(ch); copy.setCharAt(j, '*'); } } } return common.toString(); } private static int getTranspositions(final String s1Matches, final String s2Matches) { int transpositions = 0; for (int i = 0; i < s1Matches.length(); i++) { if (s1Matches.charAt(i) != s2Matches.charAt(i)) { transpositions++; } } return transpositions / 2; } private static int getCommonPrefixLength(final String s1, final String s2) { final int n = Math.min(4, Math.min(s1.length(), s2.length())); for (int i = 0; i < n; i++) { if (s1.charAt(i) != s2.charAt(i)) { return i; } } return n; } } ``` 其中,similarity方法用于计算两个字符串的相似度,返回值为一个0到1之间的double类型的数值。该方法首先判断两个字符串是否相同,如果相同则直接返回1.0;否则,根据传入的window参数获取两个字符串的匹配字符,并计算它们的相似度。其中,getMatchingCharacters方法用于获取匹配字符,getTranspositions方法用于计算转移数,getCommonPrefixLength方法用于获取两个字符串的前缀长度。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值